Errors
A handler fails only when its code returns an error, either by propagating the error from a call that engages the runtime or by returning an error explicitly.
A failed call to the runtime returns ApiError, an enum, so the code can match a case and decide what to do.
A handler returns myrmic_sdk::Result, whose error is a &'static str. Across the WebAssembly boundary a handler can only return a number, so the SDK stores the string separately.
The runtime logs a handler's failure at error level with the returned string as the message.
Example
use myrmic_sdk::{ApiError, Metadata, String};
#[myrmic_sdk::cmd]
fn stamp(_md: Metadata, label: String) -> myrmic_sdk::Result {
if label.is_empty() {
// Returned on the cell's own logic.
return Err("a label is required");
}
// `ApiError` converts into the handler's error type, so `?` propagates it.
let taken_at = myrmic_sdk::now()?;
// The error type is `&'static str`, so it takes a literal and nothing else.
let uptime = myrmic_sdk::uptime().map_err(|_| "the node's uptime is unavailable")?;
myrmic_sdk::info!("{label} at {taken_at:?}, node up for {uptime:?}")?;
Ok(())
}
fn describe(error: ApiError) -> &'static str {
match error {
// Worth retrying: the runtime cannot serve the call yet.
ApiError::NotReady => "not ready",
// The rest will fail again.
ApiError::Usage => "incorrect use",
ApiError::Serde(context) => context,
ApiError::TimedOut => "timed out",
ApiError::BufferTooSmall => "buffer too small",
ApiError::SemQuery => "invalid query",
// Every code the SDK does not name arrives here.
ApiError::UnknownErrorCode(code) => {
let _ = code;
"unknown"
}
}
}
Behavior
Normal
The failure is logged at error level, with the returned string as the message.
The runtime rolls back part of what the handler did.
Rolled back:
- everything written to storage
- every command and event the handler sent
- the message the handler was given
Left in place:
- every cell the handler spawned, and every cell it terminated
- every timer it armed or cancelled
- everything it logged
- everything it wrote to an outlet, a pin, or a Bluetooth peripheral
Afterwards, what happens to the invocation depends on what it was:
- a command - delivered again, so a command handler has to be idempotent
- an event - not delivered again, so the event is lost
- a scheduled invocation - lost, and the schedule keeps running
- a monitor notification - delivered again
- initialization - the deployment fails with that error message, and the cell does not start
API documentation
For every error variant, see ApiError. For the handler's result type, see Result.