diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 86b77a82876..55bef3f1bf7 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1571,6 +1571,10 @@ export const api: NavMenuConstant = { { name: 'Generating TypeScript Types', url: '/guides/api/rest/generating-types' }, { name: 'Generating Python Types', url: '/guides/api/rest/generating-python-types' }, { name: 'Error Codes', url: '/guides/api/rest/postgrest-error-codes' }, + { + name: 'Handling Errors in supabase-js', + url: '/guides/api/handling-errors-in-supabase-js', + }, ], }, { diff --git a/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx b/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx new file mode 100644 index 00000000000..3e816f35b86 --- /dev/null +++ b/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx @@ -0,0 +1,145 @@ +--- +id: handling-errors-in-supabase-js +title: 'Handling errors in `supabase-js`' +subtitle: 'Read `error.hint` first — Postgres often tells you the exact fix. Log the full error so you actually see it.' +--- + +Every `supabase-js` call returns a `{ data, error }` pair instead of throwing. When something fails, the single most useful field on `error` is usually `hint` — Postgres returns the _fix_, not just a description of the problem. Logging only `error.message` hides it. + +## Usage of `message` and `hint` properties + +Consider a `42501` permission-denied error on a table where default `GRANT`s have been revoked from `anon`: + +``` +message: "permission denied for table users" +hint: "Grant the required privileges to the current role with: GRANT SELECT ON public.users TO anon;" +``` + +The `message` exposes the error reason, and `hint` gives you the literal SQL statement to run in the dashboard SQL editor to fix it. + +The same pattern shows up across many Postgres errors — missing column? `hint` suggests the column name you probably meant. Type mismatch? `hint` shows the expected type. Whenever Postgres knows the fix, it puts it in `hint`. + +Log the full `error` object, not just `error.message`. + +## The recommended pattern + +Read `{ data, error }` from the response, check `error`, log the whole object, and return early. + +```ts +const { data, error } = await supabase.from('users').select() +if (error) { + console.error(error) + return +} +``` + +In the case of a permission-denied error, the response body will look like this: + +```json +{ + "error": { + "code": "42501", + "message": "permission denied for table users", + "details": null, + "hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.users TO anon;" + }, + "status": 401, + "statusText": "Unauthorized" +} +``` + +`postgrest-js` passes the body through verbatim, so `error.hint` is the exact string Postgres produced. Treat it as the answer the database is giving you, not as a suggestion to file away. + +## The `PostgrestError` fields, by usefulness + +Database calls (`select`, `insert`, `update`, `upsert`, `delete`, `rpc`) return a `PostgrestError` with four fields. Read them in roughly this order: + +| Field | Read it when | +| --------- | ------------------------------------------------------------------------------------------------------------------ | +| `hint` | Always check first. When Postgres includes one, it's the actionable fix (a `GRANT` to run, a column name, a type). | +| `code` | When branching in code. Codes are stable across versions; `message` text isn't. | +| `details` | When `hint` and `message` aren't enough. Often contains the offending value, key, or row. | +| `message` | As the human summary. Useful in UI strings, less useful for debugging. | + +A full list of PostgREST error codes is in the [Error Codes reference](/guides/api/rest/postgrest-error-codes). + +## Branch on `error.code`, not `error.message` + +`error.code` is more reliable than `error.message` for programmatic branching: messages change between Postgres and PostgREST versions, but codes are stable. + +```ts +const { data, error } = await supabase.from('users').select() +if (error) { + console.error(error) + if (error.code === '42501') { + // Permission denied. error.hint usually contains the GRANT to run. + } + return +} +``` + +## Errors from Auth, Storage, and Edge Functions + +The same rule applies across the SDK — log the whole error object — but the shape differs by client. + +### Auth + +`AuthError` exposes `error.code` (e.g. `'invalid_credentials'`, `'email_not_confirmed'`) and `error.status`. Branch on `code`; log the whole thing. + +```ts +const { data, error } = await supabase.auth.signInWithPassword({ + email: 'example@email.com', + password: 'example-password', +}) +if (error) { + console.error(error) + return +} +``` + +### Storage + +`StorageError` exposes `error.statusCode` (HTTP status as a string) and a structured `error` name (e.g. `'Duplicate'`, `'NotFound'`). + +```ts +const { data, error } = await supabase.storage + .from('avatars') + .upload('public/avatar1.png', avatarFile) +if (error) { + console.error(error) + return +} +``` + +### Edge Functions + +Functions errors arrive as one of three subclasses. Narrow with `instanceof`; for `FunctionsHttpError`, parse the body to get the function's own error payload. + +```ts +import { FunctionsFetchError, FunctionsHttpError, FunctionsRelayError } from '@supabase/supabase-js' + +const { data, error } = await supabase.functions.invoke('hello') +if (error instanceof FunctionsHttpError) { + console.error('Function error', await error.context.json()) +} else if (error) { + console.error(error) +} +``` + +### Realtime + +The `subscribe()` callback receives a `status` and, on failure, an `err` argument. Log the whole `err` — its `cause` often holds the underlying reason. + +```ts +supabase.channel('room1').subscribe((status, err) => { + if (status === 'CHANNEL_ERROR' || status === 'TIMED_OUT') { + console.error(status, err) + } +}) +``` + +## Related + +- [PostgREST Error Codes](/guides/api/rest/postgrest-error-codes) +- [Automatic retries with `supabase-js`](/guides/api/automatic-retries-in-supabase-js) +- [Securing your API](/guides/api/securing-your-api)