diff --git a/apps/docs/content/guides/api/rest/postgrest-error-codes.mdx b/apps/docs/content/guides/api/rest/postgrest-error-codes.mdx index 19288d73d08..8c91fbe3694 100644 --- a/apps/docs/content/guides/api/rest/postgrest-error-codes.mdx +++ b/apps/docs/content/guides/api/rest/postgrest-error-codes.mdx @@ -67,6 +67,19 @@ Here's the text formatted as a proper markdown table: | 42501 | if authenticated 403, else 401 | insufficient privileges | | other | 400 | | + + +Some codes in this table are triggered by platform conditions rather than application code. Seeing them in high volume usually points to an infrastructure event rather than a bug in your queries: + +- **25006** — The database has entered read-only mode due to disk quota. See [Read-only mode](/docs/guides/platform/database-size#read-only-mode) for causes and recovery steps. +- **53100** — Disk is full. Supabase emits this alongside 25006 during severe disk exhaustion. +- **53300** — Too many connections. The connection pool has reached its limit; check your [connection pool settings](/docs/guides/database/connection-management). +- **57P03** — The database cannot accept connections, typically during a restart or failover. + +When you see these codes in volume, check the [Database dashboard](/dashboard/project/_/observability/database) before debugging application code. + + + ## API level errors ### Connection errors diff --git a/apps/docs/content/guides/observability/advanced-log-filtering.mdx b/apps/docs/content/guides/observability/advanced-log-filtering.mdx index fc46a5c8b22..ecf286ca5d4 100644 --- a/apps/docs/content/guides/observability/advanced-log-filtering.mdx +++ b/apps/docs/content/guides/observability/advanced-log-filtering.mdx @@ -95,6 +95,41 @@ limit 100; Combine predicates with `and`, `or`, and `not`. Select only the fields needed for the investigation. To correlate sources, use an identifier present in both; a shared timestamp alone does not establish that events belong to the same request. +## Filter by Postgres error code [#sqlstate-filtering] + +Filter `postgres_logs` by SQLSTATE to surface errors at a specific category rather than by message text. The code lives in `log_attributes['parsed.sql_state_code']`. + +To see which error codes are occurring across a time window: + +```sql +select + log_attributes['parsed.sql_state_code'] as sqlstate, + count() as occurrences +from logs +where source = 'postgres_logs' + and log_attributes['parsed.sql_state_code'] != '' +group by sqlstate +order by occurrences desc +limit 50; +``` + +During a platform-level incident (such as the database entering read-only mode), codes like `25006` (`read_only_sql_transaction`) and `53100` (`disk_full`) will dominate the results. To focus on application errors while a platform incident is active, exclude the known platform codes: + +```sql +select + timestamp, + log_attributes['parsed.sql_state_code'] as sqlstate, + event_message +from logs +where source = 'postgres_logs' + and log_attributes['parsed.sql_state_code'] not in ('25006', '53100', '57P03') + and log_attributes['parsed.error_severity'] = 'ERROR' +order by timestamp desc +limit 50; +``` + +SQLSTATE codes starting with `25` or `53` usually indicate platform-level resource events rather than application bugs. See the [PostgREST error codes reference](/docs/guides/api/rest/postgrest-error-codes#database-level-errors) and [Database size guide](/docs/guides/platform/database-size#read-only-mode) for context. + ## Query limits [#limit-and-result-row-limitations] Use an explicit `limit` and narrow time range. The logs query surface rejects `select *` and `count(*)`; list columns and use `count()`. A result limit bounds returned rows, not the time range scanned. diff --git a/apps/docs/content/guides/platform/database-size.mdx b/apps/docs/content/guides/platform/database-size.mdx index bf8532ebd3d..65466517d3e 100644 --- a/apps/docs/content/guides/platform/database-size.mdx +++ b/apps/docs/content/guides/platform/database-size.mdx @@ -124,7 +124,24 @@ To resolve it, upgrade your plan or disable your Spend Cap to lift the restricti In some cases Supabase may put your database into read-only mode to prevent your database from exceeding the billing or disk limitations. -In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size, +In read-only mode, clients will encounter errors such as `cannot execute INSERT in a read-only transaction`. The Postgres SQLSTATE code for this error is **25006** (`read_only_sql_transaction`). During severe disk exhaustion, **SQLSTATE 53100** (`disk_full`) may also appear. Regular operation (read-write mode) is automatically re-enabled once usage is below 95% of the disk size. + +While in read-only mode, all write operations are blocked — including background jobs, scheduled tasks, and internal monitoring writes. Data that those jobs write (such as disk usage snapshots) will be stale until the database returns to read-write mode. + +To confirm that your database has entered read-only mode, query Postgres logs for the relevant SQLSTATE codes using the [Logs Explorer](/dashboard/project/_/logs/explorer) with the query source set to **Logs**: + +```sql +select + timestamp, + log_attributes['parsed.error_severity'] as severity, + log_attributes['parsed.sql_state_code'] as sqlstate, + event_message +from logs +where source = 'postgres_logs' + and log_attributes['parsed.sql_state_code'] in ('25006', '53100', '57P03') +order by timestamp desc +limit 50; +``` ### Disabling read-only mode @@ -136,7 +153,7 @@ First, change the [transaction access mode](https://www.postgresql.org/docs/curr set session characteristics as transaction read write; ``` -This allows you to delete data from within the session. After deleting data, consider running a vacuum to reclaim as much space as possible: +This allows you to delete data from within the current session. After deleting data, consider running a vacuum to reclaim as much space as possible: ```sql vacuum; @@ -148,6 +165,12 @@ Once you have reclaimed space, you can run the following to disable [read-only]( set default_transaction_read_only = 'off'; ``` + + +`SET SESSION CHARACTERISTICS` applies only to the current session. Background jobs and scheduled tasks resume writes automatically once the platform exits read-only mode — no manual step is needed. + + + ### Disk size distribution You can check the distribution of your disk size on your [project's Infrastructure page](/dashboard/project/_/settings/infrastructure).