mirror of
https://github.com/supabase/supabase.git
synced 2026-07-02 01:54:22 +08:00
New guide at `/guides/api/handling-errors-in-supabase-js` that leads
with `error.hint` as the most useful field on a Postgres error. When the
database knows the fix (a `GRANT` statement to run for a `42501`, a
column name you probably meant), it puts the literal SQL in `hint`.
Logging only `error.message` hides it.
The guide covers the `PostgrestError` shape (fields ordered by
usefulness: hint first, message last), branching on `error.code`, and
parallel patterns for Auth, Storage, Edge Functions, and Realtime.
Linked from the API > Guides sidebar next to the existing PostgREST
error codes reference.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Added a comprehensive guide on handling errors in supabase-js,
explaining the {data, error} pattern and recommending logging full error
objects.
* Provides a recommended error-handling pattern, guidance to branch on
error codes, and example error fields.
* Adds component-specific advice for Auth, Storage, Edge Functions, and
Realtime, plus a “Related” links section.
* Added a navigation entry so the guide appears in the API guides
submenu.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
146 lines
5.3 KiB
Plaintext
146 lines
5.3 KiB
Plaintext
---
|
|
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`.
|
|
|
|
<Admonition type="tip">Log the full `error` object, not just `error.message`.</Admonition>
|
|
|
|
## 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)
|