{ error, message, statusCode, nextActions } -
nextActions tells you how to fix the call. The CLI prints it automatically.
Common errors
REALTIME_UNAUTHORIZED on subscribe.
No declared channel pattern covers that channel. Fix:
npx @resultdev/cli realtime add-channel "chat:%" - the pattern alone
unlocks it.
“permission denied” on storage upload or list().
The caller is anonymous. Uploads and listing require a signed-in user - call
backend.auth.signUp(...) or signInWithPassword(...) first.
Insert silently fails or 400s.
insert() takes an array: .insert([{ title: "Hi" }]), not
.insert({ title: "Hi" }).
Signed-in queries return no rows on an RLS table (and writes 403 with
code 42501).
Most often the table has no policy: RLS with zero policies denies every
read and write, and reads fail silently as []. Recreate the table through
result db create-table with a user_id:uuid column (the owner policy is
generated for you) or add a policy with result db migrate. If a policy
exists, the rows were created without a user_id matching the session
(e.g. seeded with the admin key) - backfill it or recreate the rows as the
user.
functions.invoke() throws “Failed to fetch” in the browser.
@resultdev/sdk older than 0.3.0 routed browser calls through a retired
host. Upgrade to the latest SDK.
Realtime publishes succeed but subscribers receive nothing.
Same cause: SDK older than 0.3.0 tagged incoming messages with a prefixed
channel name, so channel filters never matched. Upgrade, and confirm the
channel pattern is declared.
AI request with an image or audio fails (AI_UPSTREAM_UNAVAILABLE).
Use google/gemini-2.5-flash - the verified model for image and audio
input. Audio must be wav/mp3/aiff/aac/ogg/flac/m4a; browser MediaRecorder’s
webm is rejected.
Sign-in works, then a reload shows the user signed out.
Almost always no getCurrentUser() call when the app mounts, so nothing
redeems the stored session. That call is what restores it, and
onAuthStateChange() does not stand in for it: it reports changes only,
never the session that already exists. If you do call it, upgrade to SDK
0.7.0 or newer - before that the session rode a cookie Safari, incognito
windows and framed previews refuse to send.
The login screen flashes on every reload for a signed-in user.
The UI has two states where it needs three. The restore is async, so render
nothing auth-dependent until getCurrentUser() resolves.
No auth token in localStorage.
By design. The access token is in memory and the refresh token is a
first-party cookie - check sign-in state with
backend.auth.getCurrentUser(), never by reading browser storage.
Migration rejected.
Names allow lowercase letters, numbers and hyphens only (the CLI normalizes
this for you), and the SQL must not contain BEGIN/COMMIT - it already
runs in one transaction.
401 from a hand-rolled HTTP call.
Don’t hand-roll - use the SDK or CLI. (If you must know: end-user calls send
Authorization: Bearer <publishable key>, not an x-api-key header.)
Debugging workflow
npx @resultdev/cli status- does the table/bucket/function exist at all?npx @resultdev/cli logs- pick a source, read recent entries.npx @resultdev/cli auth users- did the signup actually land?- Check
error.nextActionson the failing SDK call.
Facts that prevent bugs
- Every table has
id,created_at,updated_atautomatically. - Secrets are effectively permanent - deleting is unreliable; rotate by setting a new key instead.
- Email
fromis a sender name, not an address. - AI + email work with the publishable key - gate spend behind a function.