Errors and status codes
This page collects every error a client can meet: HTTP statuses from a deployment, the codes SQL queries report, the structured errors the MCP tools return, and the status values that appear in results.
A "no" that is an answer — nothing matched, the entity is unknown, a limit
was reached — is not an error. It comes back as 200 with a typed
negative in the envelope. Read that first
when a result is empty.
Error shapes
A deployment's error body always has one key, detail:
| Form | Example |
|---|---|
| String | {"detail": "body_too_large"} |
Object with code | {"detail": {"code": "forget_in_progress"}} |
Object with code and message | {"detail": {"code": "invalid_parameter", "message": "unknown argument(s): limit"}} |
| Validation list | {"detail": [{"type": "less_than_equal", "loc": ["query", "k"], "msg": "Input should be less than or equal to 400", "input": "500", "ctx": {"le": 400}}]} |
Branch on the status and the code or string, not on message.
The remember Python client raises remember.MemoryApiError for all of
them, with status_code (0 for a network failure), detail (the string, or
the message) and code (set on /query/* routes when the body carries a
known code at its expected status). A 429 raises its subclass
remember.RateLimited, whose code is rate_limited or
concurrency_limited and whose retry_after holds the Retry-After
seconds. wait_for_readiness raises
remember.PipelineDeadLettered when a stage it waits on is dead_letter
(Python SDK).
Deployment HTTP statuses
| Status | detail | Meaning | What to do |
|---|---|---|---|
400 | cursor is malformed | GET /documents got a cursor it cannot read. | Restart paging without a cursor. |
401 | a perimeter credential is required | No Authorization header, and the deployment requires one. | Send Authorization: Bearer <token>. |
401 | perimeter authentication failed | The credential is not accepted: wrong secret, bad signature, expired, revoked, or unknown signing key. | Get a fresh token. |
403 | credential is for another deployment | The credential is valid for a different deployment. | Use the token issued for this deployment, or the right REMEMBER_API_URL. |
403 | credential may not perform this operation | The credential's scope does not cover the route (for example a read credential on POST /ingest or DELETE /documents/{doc_id}, or an ingest credential on GET /deployment). | Use a write credential. See Scopes. |
404 | Not Found | No such route (includes /connectors* and /openapi.json, which a stock deployment does not serve). | Check the path. |
404 | document_not_found | DELETE /documents/{doc_id} for a document that does not exist or is already deleted. | Do not retry; check the id with GET /documents. |
404 | the operation name | POST /operations/{name} with a name that is not one of the four. | Use GET /operations for the names. |
404 | {"code": "saved_query_not_found", …} | No saved query or version by that name. | Check GET /query/saved. |
405 | Method Not Allowed | Right path, wrong method. | Check the method. |
409 | source_forgotten | POST /ingest of bytes or a source identity (source_kind + source_ref) that a hard forget removed. A forget is permanent. | Do not retry; the deployment will not take this content back. |
409 | {"code": "saved_query_…", …} | A saved query cannot run. See SQL query codes. | See the code. |
411 | length_required | POST /ingest without Content-Length on a deployment that caps bodies. | Send the length (curl and the remember client do). |
413 | body_too_large | The ingest body is over the cap. | Split the document. |
422 | validation list | A parameter or body field is missing, of the wrong type, out of range, or not declared. Includes a valid_at or believed_at without a zero UTC offset, and an unknown status on GET /query/saved. | Fix the request; loc names the field. |
422 | {"code": "invalid_parameter", …} | An operation argument or SQL-query argument was refused. | Fix the argument named in message. |
422 | source_kind and source_ref must be supplied together | Ingest with half a source identity. | Send both or neither. |
422 | source timestamps, revisions, and living mode require source_kind/source_ref | Ingest lineage options without a source identity. | Add source_kind and source_ref. |
422 | source_modified_at must be timezone-aware UTC | Ingest timestamp without an offset or with a non-zero one. | Send it with Z. |
422 | X-Ingest-Principal-Kind and X-Ingest-Principal-Ref must be supplied together | Trusted attribution with one header. | Send both or neither. |
422 | invalid_ingest_principal | Trusted attribution with an unknown kind or bad reference. | Kind is user, api_credential or service; reference is 1–255 printable ASCII characters. |
429 | {"code": "rate_limited", …} | The credential or the deployment has used its request rate (admission limits, off unless configured). | Wait the Retry-After seconds, then retry. |
429 | {"code": "concurrency_limited", …} | Too many requests of the credential or the deployment are running at once. | Wait for your other requests to finish (Retry-After is 1), then retry. |
500 | Internal Server Error | An unhandled failure: a defect. | Report it. |
503 | {"code": "forget_in_progress"} | A hard forget is running; the deployment accepts no traffic until it finishes. | Retry later. |
503 | model provider unavailable | A search, lookup, resolve or assured-operation call could not embed its query. | Retry with back-off. |
503 | live graph is busy | No graph traversal slot was free in time. | Retry with back-off. |
503 | live graph result unavailable | A traversal and the rows it pointed at disagreed. | Retry. |
503 | live graph timed out | A traversal ran past its time limit, or its database connection failed. | Retry with back-off; narrow hops, max_hops or predicates if it persists. |
503 | {"code": "…_unavailable", …} | A store needed by a SQL query was unavailable. See SQL query codes. | Retry with back-off. |
SQL query codes
SQL queries report problems with one of 27 codes. For the statement routes
(POST /query/sql, POST /query/sql/explain,
POST /query/saved/{namespace}/{name}/run), a problem with the statement
itself comes back as 200 with the code in the result's error_code and
termination_reason rejected or failed. A code raised outside the
statement — a saved query that cannot run, a refused discovery argument — is
an HTTP error at the status below, with
{"detail": {"code": "…", "message": "…"}}.
| Code | HTTP status | Meaning | What to do |
|---|---|---|---|
parse_error | 422 | The SQL does not parse, or contains a NUL byte. | Fix the syntax. |
multiple_statements | 422 | More than one statement. | Send one statement. |
statement_not_allowed | 422 | Not a read-only SELECT/VALUES/WITH, or uses a construct outside the grammar (row locks, SELECT INTO, TABLESAMPLE, WITHIN GROUP, a reserved __rememberstack_ name). | Rewrite within the grammar. |
relation_not_allowed | 422 | A table, view or schema outside memory_v1. | Use the views. |
function_not_allowed | 422 | A function not on the allowlist (including now()), a qualified function outside memory_v1/pg_catalog, a table function, an XML expression or a session keyword. | Use an allowed function; pass times as parameters. |
function_placement_not_allowed | 422 | A public function outside a top-level FROM item, packed with others in ROWS FROM, or given a computed argument. | Follow the placement rules. |
operator_not_allowed | 422 | An operator or cast type outside the allowlist. | Use an allowed operator or cast. |
invalid_parameter | 422 | Wrong parameter count, non-contiguous placeholders, too many or too large parameters, SQL text over 65,536 bytes, a bad filter, k below 1, a graph function without $1 as this deployment's id, or a graph call with only one clock. | Fix the parameters. |
unbounded_recursion | 422 | A recursive CTE that does not follow the template, or more than one. | Follow the recursion template. |
schema_version_mismatch | 409 | The database's memory_v1 views do not match the server's manifest. | An operator must finish the upgrade (migrations). |
quota_exceeded | 409 | More than 3 function calls of one category, the statement's 200-candidate search budget is spent, the per-minute statement-time budget is spent, or the operator has disabled SQL queries. | Simplify the statement or wait a minute. |
concurrency_exceeded | 409 | Too many statements running for this caller (2) or deployment (8). | Retry after the others finish. |
saved_query_not_found | 404 | No saved query or version by that name, or the deployment has no registry. | Check GET /query/saved. |
saved_query_disabled | 409 | The saved query is disabled, or the version is a draft, deprecated or broken. | Run an active version. |
saved_query_incompatible | 409 | A saved-query version failed a validation or activation check (raised by the deployment's authoring tooling, not by the HTTP routes). | Revalidate the version. |
saved_query_revalidation_pending | 409 | The query space changed since the version was validated. | Revalidate the saved query. |
statement_timeout | 500 | The statement ran past its timeout. | Narrow the statement, add filters or a LIMIT. |
lock_timeout | 500 | A lock was not available within the lock timeout. | Retry. |
cancelled | 500 | The statement was cancelled. Not produced by this release. | Retry. |
resource_limit | 500 | Memory, temporary-file or connection limits were exceeded. | Narrow the statement. |
execution_error | 500 | The statement failed while running (for example a bad cast of a parameter), or the saved-query registry could not be read. | Check parameter values and casts. |
pg_unavailable | 503 | The database is unavailable. | Retry with back-off. |
p1_unavailable | 503 | The search index could not be searched or read, or no embedder is configured. | Retry; check readiness (p1). |
graph_unavailable | 503 | A graph function returned no usable status. | Retry; check readiness (live_graph). |
corpus_body_unavailable | 503 | Chunk text could not be read. Not produced by this release. | Retry. |
generation_unavailable | 503 | The search index has no usable embedding generation, or requested chunks span more than one. | Retry after indexing completes; fetch chunks from one generation. |
confirmation_failed | 500 | Nominated rows could not be confirmed against the database. | Retry. |
When a code arrives inside a 200 result, termination_reason is rejected
if the statement was refused before the engine opened a transaction (parse
and grammar codes, unbounded_recursion, parameter-count and size problems,
quota_exceeded, concurrency_exceeded, schema_version_mismatch), and
failed if it was refused or failed after that (bad search filters,
timeouts, store codes).
MCP tool errors
The MCP server returns a tool failure as a normal tool result with
"isError": true and one JSON text block. Every tool — ingest,
pipeline_readiness, delete_document, the assured operations and the SQL
query tools — returns the same object:
{
"error": {
"code": "body_too_large",
"status_code": 413,
"detail": "Ingest body exceeds the deployment size limit.",
"retryable": false,
"agent_action": "Split or shorten the document; do not retry the same payload."
}
}status_code is the deployment's HTTP status, 0 when no answer arrived,
and null when the call never reached the deployment. reason_code,
request_id and retry_after (seconds) appear only when known. When the
deployment sent its own code — a SQL query code from the tables above, or
rate_limited / concurrency_limited — code is that code.
code | status_code | retryable | agent_action |
|---|---|---|---|
invalid_arguments | null | no | Fix the tool arguments and retry. (Or, when more than one body was given: Supply exactly one body source: path, text, or content_base64.) |
unknown_tool | null | no | Call tools/list and use one of the listed tools. |
project_routing_unavailable | null | no | Call the tool again without project. |
read_only | null | no | Tell the user this server cannot change memory. |
source_lineage_pair | null | no | Send both source_kind and source_ref, or neither. (Or: Provide source_kind and source_ref together with lineage fields.) |
empty_body | null, or the deployment's | no | Provide non-empty path / text / content_base64 content. |
encoding_error | null | no | Remove lone surrogates / invalid code points, or send content_base64 for binary. |
path_not_allowed | null | no | Pass a clean filesystem path without NUL characters. / Use text or content_base64, or ask the operator to configure REMEMBERSTACK_MCP_INGEST_ROOTS. Do not retry path until roots are set. / Place the file under an allowlisted root, or use text/content_base64. Ask the operator to extend roots only when intentional. |
path_not_regular_file | null | no | Point path at a regular file, or send text/content_base64. |
path_unreadable | null | no | Pass a regular filesystem file path. / Check path on the machine running the MCP server (not the remote engine host). |
path_too_large | null | no | Split the file, raise the local resource guard only if intentional, or use a deployment that publishes a higher capability limit. / Split the file or raise the configured read cap. |
document_not_found | 404 | no | Do not retry. Check the doc_id; if the user meant this document, it is already gone from memory. |
forget_in_progress | 503 | yes | Retry the same delete later with back-off; the deployment accepts no changes until the forget finishes. |
body_too_large | 413 | no | Split or shorten the document; do not retry the same payload. |
unauthorized | 401 | no | The key is missing, expired or revoked: run remember login or replace REMEMBER_API_KEY. |
insufficient_permission | 403 | no | The key may not do this here: use a key with the needed permission for this deployment. |
rate_limited, concurrency_limited | 429 | yes | Wait retry_after seconds (if given), then retry; do not retry sooner. Lower the request rate or run fewer calls at once. |
| SQL query codes | as sent, or null when refused before sending | for quota_exceeded, concurrency_exceeded, timeouts and store codes | Retry later with back-off. / Read the detail and fix the query or its arguments. |
engine_client_error | the HTTP status (4xx) | no | Read the detail; fix the call. Do not retry it unchanged. |
engine_unavailable | the HTTP status (5xx) | yes | Retry with back-off (3–5 attempts, 2s→30s). If still failing, report an operator outage. |
transport_error | 0 | yes | Retry with back-off; check the deployment URL and network. |
local_backend_error | null | no | Report a composition/contract defect; do not retry the same call. |
internal_error | null | no | Unexpected internal failure. Do not busy-retry; report the error (and any request_id) to an operator or as a product defect. |
JSON-RPC errors
| Code | Message | Cause |
|---|---|---|
-32700 | the parse error | The request line is not JSON. |
-32600 | invalid JSON-RPC request / request is not an object | Not a JSON-RPC 2.0 request. |
-32601 | unknown method '<name>' | A method other than initialize, ping, tools/list, tools/call. |
-32602 | bad initialize params / bad params / bad arguments | Malformed parameters. |
-32603 | the error text | tools/list could not read the deployment (unreachable, or the key was refused). |
See MCP tools.
Status values
Document version status
| Value | Meaning |
|---|---|
ingesting | Stored; conversion not started. |
converting | Being converted to Markdown. |
structuring | Being split into sections. |
ready | Converted and structured. Extraction may still be running; use readiness. |
failed | Conversion or structuring failed; error says why. |
deleted | Deleted. Not listed by GET /documents. |
Pipeline stage status
| Value | Meaning |
|---|---|
missing | No work exists yet for this stage and component version. |
pending | Queued. |
running | In progress. |
succeeded | Done. |
skipped | Not needed for this version; counts as done. |
failed | The last attempt failed and a retry is scheduled. Keep waiting. |
dead_letter | Failed for good: every attempt is used. Stop waiting and report it. The Python client's wait_for_readiness raises remember.PipelineDeadLettered here, naming the version and stage. |
Readiness capability reasons
See Ingest.
Saved query status
| Value | Runs? | Meaning |
|---|---|---|
draft | no | Written, not activated. |
pending_revalidation | no | Was active; the query space changed and it must be revalidated. |
active | yes | The version that runs. |
deprecated | no | Replaced by a newer version. |
disabled | no | Turned off. |
broken | no | Failed validation. |
SQL query termination reason
completed, rejected, failed. See SQL queries.
Connector status
active, paused, error. The connector routes are not served by a stock
deployment in this release.