Errors
HTTP status codes, error envelopes, tool errors, and session error events.
Brain separates failures into three distinct error layers so your application can tell whether an HTTP request was rejected, a tool call failed during a turn, or the turn itself stopped:
- Client request errors (
4xx/500HTTP responses): Reject bad inputs, missing permissions, or capacity limits without breaking a healthy session. - Tool errors (
tool.resultevents): Tool failures become tool results (result.is_error: true) so the model can inspect the error and decide how to proceed. - Execution errors (
session.errorevents): Brain errors and model provider failures stop the affected execution and move the session tointerruptedorfailed.
HTTP error shape
Every failed HTTP request returns a JSON error envelope containing error.code, error.message, and request_id:
{
"error": {
"code": "conflict",
"message": "resume the interrupted session before sending input"
},
"request_id": "a1b2c3d4e5f60718293a4b5c6d7e8f90"
}In the TypeScript and Python SDKs, non-2xx HTTP responses raise BrainError with status, code, message, and requestId / request_id.
HTTP status codes
| HTTP status | error.code | Meaning | What to do |
|---|---|---|---|
400 | invalid_request | Malformed JSON, unknown fields, trailing data, invalid session config, invalid pagination limit, missing/invalid checkpoint reference, or unconfigured model provider. | Fix the request body or query parameters before retrying. |
401 | forbidden | Missing or unrecognized Authorization: Bearer <token> header (also sets WWW-Authenticate: Bearer). | Supply a valid bearer token configured in tokens. |
403 | forbidden | Token is read_only, lacks manage: true for a management route, is scoped to a different session, or requests a tool not authorized for the tenant. | Use a token with the required tenant and management permissions. |
404 | not_found | Unknown route, unknown session ID, session belongs to another tenant, or external checkpoint was not found. | Verify the URL path, session ID, checkpoint ID, and tenant token. |
409 | conflict | Session is in the wrong state (for example, interrupted, failed, closed, or actively running during restore), or an idempotency key was reused with different input. | Call POST /sessions/{id}/resume before sending input, wait for active work to settle before restoring, or use a new idempotency key. |
409 | cursor_unavailable | The after or Last-Event-ID cursor is malformed, exceeds 512 bytes, or points to an unsynced event tail lost after a checkpoint restore. | Reload retained event history from the beginning (after="") to resync client state. |
413 | invalid_request | Request body exceeds limits.input_bytes + 16 KiB. | Reduce the message text or instructions length, or increase limits.input_bytes. |
422 | unsupported | Requested harness is not configured, delivery is the reserved value "steer", or flush/sync/restore was called without persistence configured. | Use a configured harness, "queue" delivery, or enable persistence in the host config. |
429 | overloaded | Session input queue (limits.queue), session log size (limits.session_bytes), loaded sessions (limits.loaded), SSE subscribers (limits.subscribers), or persistence sync queue (persistence.queue) is full, or the server is draining. | Wait for the Retry-After: 1 header window (or flush external storage if the sync backlog is full) and retry. |
500 | brain_error | Unexpected local storage, runner, or internal server failure (message is "internal server error" unless a typed internal error is returned). | Inspect the server's structured JSON logs using request_id. |
Request IDs
Every HTTP response includes an X-Request-ID header (a 32-character hex ID). When a request fails, the same value is returned in the JSON body's request_id field, and 500 responses log "request failed" with request_id to stderr. Include request_id whenever diagnosing failed requests.
Retries and Retry-After
- HTTP client requests: Neither the TypeScript SDK nor the Python SDK retries failed HTTP requests automatically. When Brain returns
429 overloaded, it sets theRetry-After: 1response header. When retryingPOST /sessions/{id}/events, pass the same idempotencykeyon youruser.messageso duplicate submissions return the originally recorded event instead of queueing a second turn. - Tool dispatches: Brain never retries tool calls automatically. A transient network failure or timeout on a tool call is reported to the model so the harness or tool service can decide whether retrying is safe.
Tool errors (tool.result)
Tool errors do not fail the HTTP request or stop the turn. When a tool call is denied, fails input/output schema validation, returns a non-2xx HTTP status, outputs invalid JSON, exceeds max_output_bytes, or times out, Brain records a tool.result event with result.is_error: true and passes the error text back to the harness:
{
"call_id": "CALL_ID",
"result": {
"content": [{ "type": "text", "text": "tool transport failed; remote outcome may be unknown" }],
"is_error": true,
"unknown_outcome": true
}
}unknown_outcome: false(omitted): The failure happened before the tool service received the request (for example, unauthorized tool, schema validation error, missing local credential, or MCP connection failure before dispatch). No remote side effect occurred.unknown_outcome: true: The failure happened after Brain sent the request (for example, network drop, timeout, HTTP 5xx, oversized response, or broken SSE stream). The remote side effect may have already happened.
Session errors (session.error)
When an execution failure stops a turn (or cancels a queued message), Brain emits a retained session.error event with input_id set to the affected user.message id, followed by a session.status event:
data.code | Resulting session status | When it fires |
|---|---|---|
provider_error | interrupted | The model provider rejected the request or failed mid-stream. |
conflict | interrupted or closed | The active turn was interrupted (user.interrupt, session close, or server shutdown), or a queued user.message was cancelled before it could run ("queued input interrupted before execution"). |
brain_error (or other internal codes) | failed | The harness process crashed, violated the stdio framing protocol, selected an unsupported model or reasoning effort, or local disk writes failed. |
To continue a session after it enters interrupted or failed, call POST /sessions/{id}/resume (brain.resume(id)) to reset the session to idle at its last completed checkpoint before sending new input.