Skip to main content

Common Errors Quick Reference

When errors occur while using the CloudBase PostgreSQL database, they mostly involve the three layers of GRANT / RLS Policy / JWT. This article lists common issues in the format "symptom → root cause → troubleshooting" for quick localization.

💡 Reading suggestion: Start with the "Quick Troubleshooting Decision Tree" to locate the broad category of the problem, then refer to the specific section.

Quick Troubleshooting Decision Tree

Request failed / no data obtained

├─ HTTP 401 ? → JWT missing / expired / signature error → see [§ JWT and Auth](#jwt)
├─ HTTP 403 ? → Insufficient table-level GRANT → see [§ Table-level GRANT](#grant)
│ → or RLS WITH CHECK rejected the write
├─ HTTP 404 ? → Table / RPC function does not exist → see [§ Resource Not Found](#not-found)
├─ HTTP 406 ? → Single-object return violates count constraint → see [§ Return Format](#return-format)
├─ HTTP 409 ? → Unique constraint / foreign key conflict → check table constraints
├─ HTTP 500 ? → RAISE inside function / instance error → see [§ Internal Error](#internal)
└─ 200 but empty data ? → RLS silently filtered → see [§ RLS Silent Filter](#silent)

JWT and Auth

401 Unauthorized / JWT expired

SymptomRoot CauseTroubleshooting
No Authorization Header at allClient forgot to attach itEnsure the frontend uses the SDK (auto-attached) or manually add Authorization: Bearer <token>
JWT expiredaccess_token expiredFrontend calls refresh or re-signs in; Publishable Key / API Key never expire
Invalid JWTSignature error, token tamperedCheck for spaces or line breaks introduced during copy-paste; confirm environment ID matches aud / project_id in the token
iss claim mismatchUsed a token from another environment across environmentsFrontend environment ID must match the token issuance environment

Check the role / sub in the Current Token

The fastest method is to use jwt.io or decode on the backend (without verification):

echo "<your-jwt>" | awk -F. '{print $2}' | base64 -d 2>/dev/null

You should see role: "anon" | "authenticated" | "service_role" and sub: "<uuid>".

Table-level GRANT

permission denied for table xxx

ERROR: permission denied for table todos
Root CauseTroubleshooting
Role lacks GRANT for the corresponding DMLCheck with \dp public.todos; as needed GRANT SELECT/INSERT/UPDATE/DELETE ON public.todos TO <role>;
RLS Policy written but GRANT missingRLS is the second layer; GRANT is the first. Both layers must pass
Used a non-business roleApplication traffic must fall into one of anon / authenticated / service_role; other system roles are unavailable

permission denied for sequence xxx_id_seq

ERROR: permission denied for sequence todos_id_seq

Cause: A serial / bigserial primary key is backed by a SEQUENCE object; granting INSERT on the table does not automatically grant sequence permission.

Fix:

GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO authenticated;
GRANT USAGE, SELECT ON SEQUENCE public.todos_id_seq TO service_role;

A table's sequence name is usually <table>_<column>_seq, viewable with \ds public.*.

RLS Policy

new row violates row-level security policy for table "xxx"

ERROR: new row violates row-level security policy for table "orders"
Root CauseTroubleshooting
WITH CHECK not satisfiedA written field does not satisfy the Policy. Most common: frontend forges owner_id/buyer_id inconsistent with JWT sub → let the database DEFAULT auto-bind, frontend should not pass it
INSERT Policy missingAfter enabling RLS, you must explicitly write an INSERT Policy for each role (or the role has BYPASSRLS)
UPDATE Policy missing WITH CHECKWhen only USING is written, the row after UPDATE is not checked; it is recommended to pair USING + WITH CHECK

200 OK but SELECT returns empty array (RLS silent filter)

Characteristic: HTTP 200, no error, but data is [] or null.

Root CauseTroubleshooting
Current role (e.g., anon) has no matching SELECT PolicyEven adding ?owner_id=eq.xxx filter won't help; RLS must be passed first
Policy condition did not match the current userUse ExecutePGSql with Role: authenticated to simulate execution and see if it matches expectations
current_setting('request.jwt.claims', true) is emptyCheck whether the request actually carried the JWT and whether the gateway successfully parsed it (generally select current_setting(...) directly in the SQL editor won't show it — it is only injected by PostgREST)

Debugging Tip: Simulate a Specific Role with ExecutePGSql

{
"EnvId": "<envId>",
"Sql": "SELECT * FROM public.todos",
"Role": "authenticated"
}

You can verify whether the Policy takes effect without actually sending an HTTP request.

Resource Not Found

404 Not Found when calling RPC

Root CauseTroubleshooting
Function name misspelledExactly the same as the CREATE FUNCTION name in SQL (including schema)
Function not under public schema and db-schemas not configuredBy default only functions under public are exposed by PostgREST
Function just created, PostgREST metadata not refreshedWait a few seconds and retry, or try to notify the backend to refresh
Parameter signature mismatchPostgREST addresses by parameter name + type; all keys in the JSON body must exactly match the function argname

404 Not Found when accessing a table

{"message":"relation \"public.todoes\" does not exist"}
  • Table name, schema misspelled
  • Table not in public schema (need Accept-Profile: <schema> Header to switch)

Return Format

406 Not Acceptable JSON object requested, multiple (or no) rows returned

{"message":"JSON object requested, multiple (or no) rows returned"}

Cause: You set Accept: application/vnd.pgrst.object+json, but the result is not exactly 1 row.

Fix: Either remove that Header, or use .single() / .maybeSingle() and ensure the query condition precisely matches 1 row.

Get the full row after writing

Add Prefer: return=representation:

curl -X POST '.../v1/rdb/rest/orders' \
-H 'Authorization: Bearer <token>' \
-H 'Prefer: return=representation' \
-H 'Content-Type: application/json' \
-d '{"product_id":1, ...}'

ExecutePGSql Errors

InternalError executing DDL failed

Some DDL statements (CREATE/ALTER/DROP/GRANT/REVOKE/TRUNCATE/COMMENT, etc.) may return InternalError when executed directly.

Fix: Wrap with DO LANGUAGE plpgsql $$ BEGIN EXECUTE '...'; END $$ and retry. This requires BEGIN and dynamic EXECUTE, so it explicitly uses LANGUAGE plpgsql:

DO LANGUAGE plpgsql $$
BEGIN
EXECUTE 'CREATE TABLE public.products (id serial PRIMARY KEY, name text)';
END
$$;

Single quotes ' inside the string need to be escaped as ''.

Each API call can only execute one SQL statement

ExecutePGSql does not support multiple statement concatenation; split and call them one by one by ;.

permission denied (when the Role parameter is present)

After adding the "Role": "authenticated" parameter, it is executed as a normal role; this triggers GRANT/RLS. This is not a bug — it is exactly its purpose. To bypass permission debugging, remove the Role parameter.

Internal Error

500 Internal Server Error from function RAISE

{"message":"Product does not exist or insufficient stock"}

This is a business error thrown by RAISE EXCEPTION '...' inside the function body — a normal failure path. The frontend can handle it according to the business message.

Instance unavailable / 503

The database instance has a temporary fault or restart. The client should retry with exponential backoff.

Gateway Errors

INVALID_REQUEST / PERMISSION_DENIED / RESOURCE_NOT_FOUND

Refer to the error code table in HTTP API - PostgREST RESTful API and the correspondence with native PostgreSQL errors:

Gateway Error CodeHTTPMeaning
INVALID_PARAM400Invalid request parameter
INVALID_REQUEST400 / 406Invalid request body / single-object count constraint not satisfied
PERMISSION_DENIED401 / 403Authentication failed / insufficient permission
RESOURCE_NOT_FOUND404Table / function does not exist
SYS_ERR500Internal system error
OPERATION_FAILED503Database connection failed
RESOURCE_UNAVAILABLE503Database unavailable

General Troubleshooting Checklist

When encountering database-related issues, 80% can be located by self-checking in the following order:

  1. Is the JWT attached? Authorization: Bearer <token> cannot be omitted
  2. Is the role in the JWT as expected? Decode the payload with base64 to take a look
  3. Is the table-level GRANT given to the corresponding role? Check with \dp <table>
  4. Is RLS enabled? SELECT relname, relrowsecurity FROM pg_class WHERE relname='xxx';
  5. Does the role + operation have a matching Policy? View the Policy list with \d+ <table>
  6. Does the Policy's USING / WITH CHECK actually hold for the current user? Simulate execution with ExecutePGSql using Role
  7. Did you forget to grant SEQUENCE permission for the serial primary key?

Next Steps