Skip to content

Gotchas

The short list. Each item links to the page that explains it.

  • u64, i64 and Vec<u8> travel as decimal strings and base64url, in arguments, results, record fields, errors and events. A client sends {"size": "7"}, not {"size": 7}.
  • The same types nested in another type (Vec<u64>, Option<Vec<i64>>) do not compile: wrap them in a Record or send strings (SDK macros).
  • A u64 inside your own plain serde struct is invisible to the macro and reaches JSON as a number, which JavaScript may round. Use a Record, whose derive converts it.
  • Arguments are camelCase (room_id → roomId), and unknown keys are refused (BadArgs).
  • Renaming a state field or changing its type orphans its data: an upgrade keeps the state and has no migration step (Upgrades & state).
  • A record must not also derive Serialize/Deserialize: #[derive(Record)] writes them.
  • Uniqueness belongs in the key or a unique(..) constraint, not in a check before an insert.
  • Prefer env::unique_id() to a Counter for ids.
  • Multi-row reads are pages of at most 1,000; follow next (Tables & Collections).
  • A method with no attribute admits any signed-in account the node lets reach the app; org membership is the app’s to check with guards (Access control).
  • Take the caller from env::caller() or a guard, never from an argument.
  • #[app::error] and #[app::event] must be in the #[app::logic] module, or the ABI lists none of them.
  • A call that fails writes nothing and emits nothing; a panic is a trap, not an app error (Error handling).
  • env::xcall only queues a message: it is delivered after the entry commits, and only if the run succeeds (Cross-app calls).
  • Only views may search; a call that searches traps (Search).
  • env::fetch, actions and cross-app calls answer an error on the host target; test them with TestApp (Testing).
  • A read-only team member reaches an app’s views only: a call to any other method is refused with 403 before it runs.