Error handling
Every method returns app::Result<T>, which is Result<T, AppError>
(crates/sdk/src/error.rs). An AppError is a code plus fields, the
shape a client receives: { code, message }, with message the fields as a
JSON object ({} when there are none).
A call that fails writes nothing: the node applies a call’s changes only when
it ends Ok, and a failed call emits no events.
The app’s own errors
Section titled “The app’s own errors”Declare them once, in the #[app::logic] module:
#[app::error]pub enum Error { NotFound, Taken { start: u64 }, Invalid(String),}The variant name is the code; named fields keep their camelCase names, tuple
fields are "0", "1"; u64, i64 and bytes are strings. Taken { start: 7 }
reaches the client as code Taken, message {"start":"7"}. The ABI lists every
code and its fields.
Return one with app::bail! or ?:
if title.is_empty() { app::bail!(Error::Invalid("empty title".into()));}self.bookings.get(&id)?.ok_or_else(|| Error::NotFound.into())For an ad-hoc error, AppError::new("Code") or
AppError::with_fields("Code", json!({ .. })).
Errors the SDK produces
Section titled “Errors the SDK produces”| Source | Code |
|---|---|
guards::require_member / require_admin |
LoginRequired (not an account), Forbidden (below the role) |
A Table write |
Duplicate with the unique constraint’s name, or "key" |
| Storage (encoding, limits, cursors) | the StorageError variant, e.g. KeyTooLong, OutOfBounds, BadCursor |
files and std::io |
Files, or Io for other io errors |
| Cross-app calls | Xcall, XcallFailed (carrying the target’s error) |
| Outbound HTTP | Fetch |
StorageError, TableError and std::io::Error convert into AppError, so
? works on every SDK call.
Errors the dispatcher produces
Section titled “Errors the dispatcher produces”Before your method runs, the generated handle refuses an unknown method
(UnknownMethod), malformed arguments (BadArgs), a call run as a view
(NotAView), and a caller the method does not allow (Forbidden,
LoginRequired).
A panic, running out of fuel or memory, or any other trap ends the run without an app error; the call fails and writes nothing. Use errors for anything a client should handle, and keep panics for bugs.