{ 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.
The rows were created without a user_id matching the session (e.g. seeded
with the admin key). RLS scopes by user_id - backfill it or recreate the
rows as the user.
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.