Skip to main content
Every backend error returns { 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

  1. npx @resultdev/cli status - does the table/bucket/function exist at all?
  2. npx @resultdev/cli logs - pick a source, read recent entries.
  3. npx @resultdev/cli auth users - did the signup actually land?
  4. Check error.nextActions on the failing SDK call.

Facts that prevent bugs

  • Every table has id, created_at, updated_at automatically.
  • Secrets are effectively permanent - deleting is unreliable; rotate by setting a new key instead.
  • Email from is a sender name, not an address.
  • AI + email work with the publishable key - gate spend behind a function.