ApiError. Each ApiError carries
three fields:
code— the API’s stable machine-readable identifier (e.g.refresh_invalid). May beundefinedif the response body was missing or malformed.status— the HTTP status code (e.g.401,404).message— human-readable, may reword.
code for recovery logic; show message in toasts /
banners; use status for catch-all 4xx-vs-5xx checks.
Import
Usage
app/page.tsx
Error codes
The codes below are stable across SDK minor versions. Catch-all onstatus for codes not listed (rare; the API tries to emit a stable
code on every 4xx).
Session
| code | status | when |
|---|---|---|
refresh_invalid | 401 | The browser’s refresh cookie is missing, expired, or wallet-mismatched. Sign out at your wallet/identity layer and remount the provider. |
invalid_token | 401 | The Privy / Dynamic identity token failed verification (bad signature, wrong audience, expired). |
token_required | 401 | The session-create request arrived without an Authorization header. |
origin_not_allowed | 403 | Request Origin isn’t in the credential’s allowlist. Check your Calm dashboard. |
wallet_not_linked | 403 | The wallet on the session request isn’t among the Privy/Dynamic user’s linked accounts. Often the identity-token toggle is off, so the wallet didn’t ride inside the JWT. |
publishable_key_missing | 400 | No publishable key reached the API — usually a bundler stripping the env var. Check NEXT_PUBLIC_CALM_KEY. |
invalid_publishable_key | 400 / 401 | calmKey is malformed (400) or unknown / revoked (401). Check your Calm dashboard. |
binding_expired | 401 | The SIWE nonce expired (5-minute TTL) before the user signed. Re-mount or retry. |
siwe_invalid | 401 | The signed SIWE message failed verification — wrong nonce, wrong recovered address, time bounds, or unparseable input. |
Wallet
| code | status | when |
|---|---|---|
session_required | 401 | No calm_session cookie on a cookie-credentialed read. The SDK gates these reads on the session being live, so seeing this in a hook’s error field usually means the session was cleared mid-render or the provider mounted without a wallet address. |
session_invalid | 401 | The session cookie was present but failed verification. |
invalid_wallet | 400 | The :address path parameter isn’t a valid Ethereum address. |
validation_error | 400 | Body parsed as JSON but failed zod validation. message carries the field-level details. |
Retry behaviour
The SDK’s hooks retry 5xx and network errors up to 3 times with react-query’s default backoff. 4xx is treated as terminal — codes likeinvalid_wallet or validation_error describe a fixed state, not a
transient failure, so retrying just spams the console and the server.