bad_request (HTTP 400) with { code: "bad_request", detail, request_id }. The SDK throws with detail — a human-readable message string, not a typed error object. Match on the substring shown below.
User-authentication failures additionally carry a machine code at the front of detail (e.g. eth_authenticator_limit: …), so the SDK can branch with a single startsWith.
Tenant operations — register, maps, dispatch
You’ll see in detail | Cause | Fix |
|---|---|---|
version <x> is not higher than current version <y> | Re-registering a contract at a version that isn’t greater than the deployed one | Bump the version passed to contracts.register |
map already exists | Re-running maps.create against an already-provisioned tenant | Idempotent — safe to ignore on re-runs |
map not found | A map tail in kv_store::get / put doesn’t match what maps.create created | Match the tails exactly between Create tenant KV maps and your Rust |
canonical map name invalid: <reason> | tail is empty, contains .., or starts with z: | Pass only the local tail (e.g. "secrets") — the SDK prefixes z:<tid>: |
quota exceeded: <dim> (e.g. quota exceeded: max_contracts) | Hit a per-tenant quota | Ask the cluster operator to raise the quota |
access denied: <caller> cannot <op> map "<map>" | The contract isn’t on the map’s readers / writers ACL | tenant.maps.update to add the contract id to readers / writers |
tenant is suspended | The operator suspended your tenant | Ask the operator to resume |
host/http.egress_denied: host '<host>' is not in the authorised_hosts allowlist | The contract called a host the caller’s agent_auth grant doesn’t authorize | Add the host to the user’s grant (see Invoke your contract) |
Contract-authored errors are whatever your contract returns. The flight
example, for instance, surfaces
duffel_api_key not found in z:<tid>:secrets — populate it via the tenant SDK when the secrets map wasn’t seeded (see
Seed API key into secrets map) — that’s
the contract’s own message, not a platform error.Authentication & wallet linking
These come from the user/session contract during sign-in andaddAuthMethod, with the code at the front of detail:
Code (prefix of detail) | When |
|---|---|
eth_authenticator_limit | Hit the cap on wallets linked to one DID (e.g. trying to add an 11th) |
eth_auth_map_conflict | The wallet is already linked to a different DID — resolve via account merge |
email_not_verified | A profile upsert ran before the email OTP was verified |
user_not_found | The DID has no profile yet |
legacy_field | A pre-2.0.0 dispatch field was sent (e.g. otp_code on user-upsert) |