Error Codes & Troubleshooting
There are two families of errors here, and they behave differently.
Plan and quota responses come back from the HTTP request itself, before or instead of a run. They carry a JSON body with a code field, and each one has a specific action that clears it. These are the ones a new account meets first, so they come first below.
Run failures happen inside a run that was accepted and started. They arrive as an error event on the stream (or on the finished run) with an error_code, and they’re grouped into five recovery classes.
Plan and quota responses
When a request is refused for plan reasons, the response body is a JSON object with a code. Branch on code, never on the HTTP status alone — 429 is shared between the rate limiter (which returns a plain string) and the quota system (which returns one of these objects), and 402 is shared by several distinct situations.
| Code | Status | What happened | What clears it |
|---|---|---|---|
quota_exceeded (scope: "month") | 429 | You’ve used every question your plan includes this calendar month. The body carries limit, used, and resets_at. | Wait for the counter to reset at 00:00 UTC on the 1st of next month, or upgrade — the new allowance applies immediately. On a Team plan the pool is shared across the workspace, so more seats also raise it. |
quota_exceeded (scope: "day") | 429 | Pro and Team carry a per-person fair-use ceiling of 500 questions per day. The body carries resets_at. | Wait for the ceiling to reset at midnight UTC. Your monthly allowance is untouched. |
discovery_limit | 429 | You’ve used every AI schema analysis your plan includes this month. The body carries limit, used, and resets_at. | Wait for the monthly reset, or upgrade. Re-running structure-only discovery, resuming a checkpointed analysis, and retrying one that reuses existing introspection are all free — only a fresh schema analysis counts. |
connection_limit | 402 | Creating, duplicating, or activating a connection would exceed your plan’s active-connection limit. With reason: "over_limit", it means you already hold more active connections than your plan allows — typically after a downgrade — and questions are blocked until you’re back under it. | Pause a connection you don’t need right now (Connections page, or POST /api/v1/connections/{id}/paused), or upgrade. Pausing keeps the connection’s schema analysis and history. |
connection_paused | 402 | The connection this request targets is paused. Paused connections can’t run questions or schema discovery. | Activate it from the Connections page — which needs room under your connection limit — or target a different connection. |
seats_full | 409 | An invite would exceed the seats on the Team subscription, or the workspace filled up between the invite being sent and accepted. The body carries seats and members. | Add seats in the Stripe customer portal, or remove a member or a pending invite, then re-send. |
team_min_seats | 422 | A Team checkout was requested with fewer than 2 seats. | Check out with at least 2 seats, or pick Lite or Pro for a single user. |
plan_blocked | 402 | An action that requires an active Team subscription was attempted without one — inviting teammates is the case you’ll actually meet. | Start or restore a Team subscription. Note this code never means your account is suspended: an expired trial or a cancelled subscription lands you on the Free plan with your data intact, not in a blocked state. |
Two things worth knowing about quota counting:
- Counters increment when a request is accepted, so a run that then fails still consumes a question.
- If the counter store is briefly unavailable, requests are allowed through rather than refused.
Plan limits themselves — how many questions, connections, and AI schema analyses each plan includes — are in Billing & plans.
Run failures
Once a run has started, failures are classified into five classes for recovery UX:
- NO_RESULTS — the question was understood but produced no useful data.
- TIMEOUT — execution exceeded its time limit.
- PLAN_FAILURE — the model could not produce a valid plan.
- CONNECTION_FAILURE — the database could not be reached.
- INTERNAL — generated SQL/code was invalid, the sandbox crashed, or the model returned malformed output.
Every error event in the run stream carries an error_code that maps to one class.
NO_RESULTS
| Code | Meaning | Recovery |
|---|---|---|
MISSING_TABLE | Generated SQL references a table not in scope | Add the missing table to scope and retry. |
MISSING_COLUMN | Generated SQL references a column that doesn’t exist | Check your table selection or rephrase. |
TYPE_MISMATCH | Implicit cast failed | Rephrase to avoid mixing incompatible types. |
The frontend surfaces these as “broaden your question” suggestions.
TIMEOUT
| Code | Meaning | Recovery |
|---|---|---|
SQL_TIMEOUT | Database query exceeded the query time limit (30 seconds) | Add date filters or narrow scope. |
SANDBOX_TIMEOUT | Sandbox Python step exceeded its wall-clock cap (90 seconds) | Simpler analysis, or reduce the data volume it works on. |
PLAN_FAILURE
| Code | Meaning | Recovery |
|---|---|---|
NOT_ANSWERABLE | The model determined the question can’t be resolved with the data in scope | Rephrase, or bring the missing tables into scope. |
AMBIGUOUS_QUERY | Multiple valid interpretations | Be more specific. |
The frontend offers an editable retry — your question stays in the input ready to revise.
CONNECTION_FAILURE
| Code | Meaning | Recovery |
|---|---|---|
CONNECTION_ERROR | Database unreachable | Test from sidebar; check credentials. |
The frontend’s recovery banner has a “test connection” button.
INTERNAL
| Code | Meaning |
|---|---|
SQL_SYNTAX | Generated SQL didn’t parse |
SQL_VALIDATION | Generated SQL failed our validation rules |
SQL_EXECUTION | Database returned an error |
SANDBOX_CODE_ERROR | Python in the sandbox raised |
SANDBOX_OOM | Python in the sandbox exceeded memory cap |
LLM_INVALID_OUTPUT | The model returned a plan that didn’t match the required structure |
Most of these trigger an automatic revise-and-retry, within strict limits. Once that budget is exhausted you see the underlying error.
Related
- Billing & plans — what each plan includes and how pausing a connection frees a slot.
- REST API reference — the endpoints these responses come from.
- Security — why some questions are refused before they run.