Error Code Reference
DB9 REST API errors return a JSON body with a human-readable message:
{ "message": "human-readable message describing what went wrong"}Use the HTTP status code for programmatic error handling. Use message for displaying messages to users or for debugging.
Two kinds of failure do not use that shape:
-
A request body that cannot be read — invalid JSON, a missing required field, a field of the wrong type, or no
Content-Type: application/jsonheader — is rejected with a plain-text body (status400,422or415, below). Do not parse these as JSON. -
A SQL error in
POST /customer/databases/{database_id}/sqlis not an HTTP error. The request returns200, with"command": "ERROR"and PostgreSQL’s message and SQLSTATE inerror:SQL error (status 200) {"columns": [],"rows": [],"row_count": 0,"command": "ERROR","error": "ERROR: syntax error at or near \"SELEC\" | sqlstate: 42601"}Check
command(or the presence oferror) on every SQL response.
Status Codes
Section titled “Status Codes”200 OK
Section titled “200 OK”The request succeeded. The response body contains the requested resource or operation result.
{ "id": "db_01h9xxxxxxxxxxxxxxxx", "name": "myapp", "state": "ACTIVE"}400 Bad Request
Section titled “400 Bad Request”The request body is not valid JSON, or a field holds a value the endpoint rejects.
{ "message": "Provide non-empty 'query' or 'file_content'"}| Common cause | Resolution |
|---|---|
Body is not valid JSON — the response is plain text, starting Failed to parse the request body as JSON: | Fix the JSON syntax |
SQL execution with neither query nor file_content non-empty | Send the SQL in one of them. If both are set, query is run and file_content is ignored |
Invalid scope_json format | Ensure the value is a valid JSON string, not an object |
401 Unauthorized
Section titled “401 Unauthorized”The request has no Authorization header, the token is expired, or the token is invalid.
{ "message": "Invalid or expired token"}| Common cause | Resolution |
|---|---|
Missing Authorization: Bearer <token> header (Authorization header required. Use Bearer <token>.) | Add the header to your request |
| Token has expired | Create a new token with db9 token create or POST /customer/tokens |
| Wrong token (e.g. publishable key used for management API) | Use your API token (DB9_API_KEY), not a publishable or service key |
| Anonymous secret mismatch | Verify anonymous_secret matches the value returned at registration |
403 Forbidden
Section titled “403 Forbidden”The token is valid but lacks the scope required to perform the operation.
{ "message": "token does not have access to this database"}| Common cause | Resolution |
|---|---|
| Token scoped to specific databases, accessing another | Check scope_json on the token — create a new token with broader scope if needed |
| Read-only token attempting a write operation | Create a token without the ro access restriction |
404 Not Found
Section titled “404 Not Found”The requested resource does not exist, or the authenticated customer does not own it.
{ "message": "Database not found"}| Common cause | Resolution |
|---|---|
| Database ID typo or copy-paste error | Double-check the database_id in your request path |
| Database was deleted | Check your database list with GET /customer/databases |
| Resource belongs to a different account | Ensure you are using the correct API token |
| Key or run ID from a different database | Verify all IDs are from the same database |
409 Conflict
Section titled “409 Conflict”The operation conflicts with existing state — typically a duplicate name or a state precondition.
{ "message": "Database name already exists for this account"}| Common cause | Resolution |
|---|---|
| Creating a database/branch with a name that already exists | Choose a different name or delete the existing resource first |
| Applying a migration that was already applied | Check applied migrations with GET /customer/databases/{id}/migrations |
| Creating a secret that already exists | Use PUT to update an existing secret, not POST |
| Adopting a database already owned by your account | The database is already in your account — no action needed |
415 Unsupported Media Type
Section titled “415 Unsupported Media Type”The request has a body but no Content-Type: application/json header. The response is plain
text: Expected request with `Content-Type: application/json` . Add the header.
422 Unprocessable Entity
Section titled “422 Unprocessable Entity”The request is syntactically valid but semantically incorrect — the server understands the request but cannot process it.
Failed to deserialize the JSON body into the target type: missing field `name` at line 1 column 2| Common cause | Resolution |
|---|---|
Missing required field — missing field `name` | Check the endpoint’s field table and include all required fields |
Field of the wrong type — invalid type: integer `123`, expected a string | Verify your request body matches the documented types |
| Invalid cron expression in function deploy | Validate the cron string (e.g. use crontab.guru) |
| Quota exceeded (databases, branches, keys) | Check your plan limits or delete unused resources |
| Invalid JWKS URL in auth config | Ensure the URL returns a valid JWKS JSON document |
snapshot_at timestamp outside available history window | Use a more recent timestamp for point-in-time branching |
429 Too Many Requests
Section titled “429 Too Many Requests”The request was rate limited. Slow down and retry after the indicated delay.
{ "message": "rate limit exceeded, retry after 30 seconds"}| Common cause | Resolution |
|---|---|
| Too many anonymous account registrations from one IP | Wait before registering more anonymous accounts |
| Function invocation rate limit | Add a delay between invocations or reduce call frequency |
| Burst of management API requests | Implement exponential backoff with jitter |
The Retry-After response header (when present) indicates the number of seconds to wait before retrying.
500 Internal Server Error
Section titled “500 Internal Server Error”An unexpected error occurred on the DB9 servers.
{ "message": "internal server error"}| Common cause | Resolution |
|---|---|
| Transient server error | Retry the request with exponential backoff |
| Persistent 500 on a specific operation | Contact support via Discord with the request details |
Handling Errors in Code
Section titled “Handling Errors in Code”TypeScript
Section titled “TypeScript”import { createDb9Client } from 'get-db9';
const client = createDb9Client();
try { const db = await client.databases.create({ name: 'myapp' });} catch (err) { if (err.status === 409) { // Database already exists — retrieve it instead const list = await client.databases.list(); const existing = list.databases.find(d => d.name === 'myapp'); } else if (err.status === 429) { // Rate limited — implement retry logic console.error('Rate limited:', err.message); } else { throw err; }}response=$(curl -s -w "\n%{http_code}" \ -H "Authorization: Bearer $DB9_API_KEY" \ -X POST https://api.db9.ai/customer/databases \ -H "Content-Type: application/json" \ -d '{"name": "myapp"}')
body=$(echo "$response" | head -1)status=$(echo "$response" | tail -1)
if [ "$status" = "409" ]; then echo "Database already exists"elif [ "$status" = "201" ] || [ "$status" = "200" ]; then echo "Created: $body"else echo "Error $status: $body"fiNext Steps
Section titled “Next Steps”- REST API Reference — all endpoints with request/response examples
- TypeScript SDK — typed client with built-in error handling
- CLI Reference — command-line interface