{
  "$comment": "Every stable CriogenAI error code (design 0013 §2). Codes may be added, never renamed or removed before 1.0. Checked against the Rust sources, docs/errors.md and both SDKs by tests.",
  "version": 1,
  "codes": [
    {"code": "node_failed", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A workflow node returned an error."},
    {"code": "node_panicked", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A Rust node panicked; the run failed instead of the process."},
    {"code": "node_timeout", "layers": ["run"], "category": "developer", "retryable": true, "family": "other", "description": "A node attempt took longer than its policy timeout."},
    {"code": "router_failed", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A router function returned an error."},
    {"code": "unknown_route", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A router returned a label with no edge."},
    {"code": "missing_goto", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A node that must choose the next node did not."},
    {"code": "memo_type_mismatch", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A saved task memo entry does not have the shape the node asks for (or a value to save is not JSON); seen as the kind of a node_failed error."},
    {"code": "update_conflict", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "Two nodes wrote the same state key in one step without a reducer."},
    {"code": "step_limit_exceeded", "layers": ["run"], "category": "policy", "retryable": false, "family": "limit", "description": "The run reached its step limit."},
    {"code": "deadline_exceeded", "layers": ["run"], "category": "policy", "retryable": false, "family": "limit", "description": "The run reached its deadline, or the next retry would pass it."},
    {"code": "cancelled", "layers": ["run", "tool_result"], "category": "cancellation", "retryable": true, "family": "cancelled", "description": "The run was cancelled (a cancel token, an AbortSignal, a dropped stream, an interrupt from another process). In a tool message: a call that never got a result."},
    {"code": "invalid_value", "layers": ["run", "storage"], "category": "developer", "retryable": false, "family": "validation", "description": "A value was not accepted: a state write a reducer cannot apply, a memory key, value, namespace or vector, a configuration value."},
    {"code": "resume_invalid", "layers": ["run"], "category": "developer", "retryable": false, "family": "validation", "description": "A resume or approval decision does not match what the run waits for."},
    {"code": "workflow_mismatch", "layers": ["run"], "category": "developer", "retryable": false, "family": "validation", "description": "A snapshot was made by a different workflow."},
    {"code": "storage_failed", "layers": ["run"], "category": "transient", "retryable": true, "family": "storage", "description": "Saving the run's progress failed; details carry the storage code."},
    {"code": "model_failed", "layers": ["run"], "category": "provider", "retryable": null, "family": "model", "description": "A model call failed after retries and fallbacks; details.model_error has the kind, and retryable follows it."},
    {"code": "tool_failed", "layers": ["run", "tool_result"], "category": "developer", "retryable": false, "family": "tool", "description": "A tool failed with an error it did not handle (an exception, a panic). By default the model sees it as the call's result and the run goes on; with on_tool_exception=\"fail\" (or ToolError::fatal) it fails the run."},
    {"code": "limit_exceeded", "layers": ["run"], "category": "policy", "retryable": false, "family": "limit", "description": "A model-call, tool-call, token or nesting limit was reached; details.limit names it."},
    {"code": "output_invalid", "layers": ["run"], "category": "policy", "retryable": false, "family": "other", "description": "The structured output was still invalid after the allowed retries."},
    {"code": "hook_failed", "layers": ["run"], "category": "developer", "retryable": false, "family": "other", "description": "A hook returned an error."},
    {"code": "invalid_state", "layers": ["run"], "category": "developer", "retryable": false, "family": "validation", "description": "The run state is not an agent's, or memory cannot find the run's namespace."},
    {"code": "invalid_input", "layers": ["run"], "category": "developer", "retryable": false, "family": "validation", "description": "The input names one of the agent's own state keys, or tries to change the value that scopes the thread's memory."},
    {"code": "audit_failed", "layers": ["run", "storage"], "category": "transient", "retryable": true, "family": "storage", "description": "An audit record could not be written, so the action it describes failed."},

    {"code": "thread_busy", "layers": ["thread", "storage"], "category": "transient", "retryable": true, "family": "busy", "description": "Another run holds the thread (OnBusy reject, or the wait timed out)."},
    {"code": "thread_suspended", "layers": ["thread"], "category": "developer", "retryable": false, "family": "suspended", "description": "The thread waits for approval: answer it before sending more."},
    {"code": "thread_interrupted", "layers": ["thread"], "category": "developer", "retryable": false, "family": "other", "description": "The thread's last run did not finish: continue it with retry."},

    {"code": "storage_unavailable", "layers": ["storage"], "category": "transient", "retryable": true, "family": "storage", "description": "The database could not be reached or timed out."},
    {"code": "storage_conflict", "layers": ["storage"], "category": "transient", "retryable": true, "family": "storage", "description": "Another writer changed the thread first."},
    {"code": "checkpoint_not_found", "layers": ["storage"], "category": "developer", "retryable": false, "family": "storage", "description": "No checkpoint with that id."},
    {"code": "storage_corrupt", "layers": ["storage"], "category": "internal", "retryable": false, "family": "storage", "description": "Stored data could not be read."},
    {"code": "storage_incompatible", "layers": ["storage"], "category": "developer", "retryable": false, "family": "storage", "description": "The database was written by a newer schema version."},
    {"code": "encryption_key_missing", "layers": ["storage"], "category": "developer", "retryable": false, "family": "storage", "description": "Encrypted data, or an encrypted database, without the key to read it."},
    {"code": "encryption_key_invalid", "layers": ["storage"], "category": "developer", "retryable": false, "family": "storage", "description": "A key does not match the one the database knows under its id."},

    {"code": "rate_limited", "layers": ["model"], "category": "transient", "retryable": true, "family": null, "http_status": 429, "description": "The provider asked us to slow down."},
    {"code": "overloaded", "layers": ["model"], "category": "transient", "retryable": true, "family": null, "http_status": 503, "description": "The provider is overloaded."},
    {"code": "server", "layers": ["model"], "category": "transient", "retryable": true, "family": null, "http_status": 502, "description": "The provider failed (5xx)."},
    {"code": "timeout", "layers": ["model", "tool_result"], "category": "transient", "retryable": true, "family": null, "http_status": 504, "description": "No answer in time: a model call, or (in a tool message) a tool call."},
    {"code": "network", "layers": ["model"], "category": "transient", "retryable": true, "family": null, "http_status": 502, "description": "The connection failed or broke."},
    {"code": "auth", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "The provider refused the API key."},
    {"code": "permission", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "The key may not use this model or feature."},
    {"code": "not_found", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "The provider does not know the model or endpoint."},
    {"code": "invalid_request", "layers": ["model", "gateway"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 400, "description": "The provider (or the gateway) rejected the request as malformed."},
    {"code": "context_overflow", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 400, "description": "The conversation is longer than the model's context."},
    {"code": "content_filter", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 400, "description": "The provider's safety system refused the request."},
    {"code": "quota_exhausted", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "Billing or quota is exhausted; another provider may still answer."},
    {"code": "invalid_response", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "A successful response whose body could not be read (already billed, so not retried)."},
    {"code": "unknown", "layers": ["model"], "category": "provider_fatal", "retryable": false, "family": null, "http_status": 502, "description": "An unclassified provider failure."},

    {"code": "invalid_arguments", "layers": ["tool_result"], "category": "model_recoverable", "retryable": false, "family": null, "description": "The model's arguments did not match the tool's schema; the model sees the problems and may correct them."},
    {"code": "unknown_tool", "layers": ["tool_result"], "category": "model_recoverable", "retryable": false, "family": null, "description": "The model called a tool the agent does not have."},
    {"code": "tool_error", "layers": ["tool_result"], "category": "model_recoverable", "retryable": false, "family": null, "description": "The tool reported a failure meant for the model (ToolError)."},

    {"code": "invalid_agent", "layers": ["build"], "category": "developer", "retryable": false, "family": "validation", "description": "An SDK agent configuration the engine could not build."},
    {"code": "invalid_workflow", "layers": ["build"], "category": "developer", "retryable": false, "family": "validation", "description": "An SDK workflow that does not validate (`details.issues` lists the problems)."},
    {"code": "unsupported", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "The feature needs a server, and here the engine runs as WebAssembly in a JavaScript host (the browser, design 0021): databases (SQLite, Postgres), encryption at rest, telemetry export, web tools."},
    {"code": "mcp_connect", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "An MCP server could not be started or reached, or refused the credentials (design 0022)."},
    {"code": "mcp_protocol", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "An MCP server answered something the client cannot use: a malformed message, an error, or a protocol version CriogenAI does not speak."},
    {"code": "mcp_tool_name", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "Two of an MCP server's tools map to the same name, or a name maps to nothing: set a prefix or leave one out with `except`."},
    {"code": "mcp_tool_schema", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "An MCP tool's input schema is not an object schema, or is too large or too deep: leave the tool out with `except`."},
    {"code": "mcp_timeout", "layers": ["build"], "category": "transient", "retryable": true, "family": "other", "description": "An MCP server was not ready within the connect timeout."},
    {"code": "mcp_closed", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "The MCP server stopped (its process exited) or the client was closed."},
    {"code": "eval_failed", "layers": ["build"], "category": "developer", "retryable": false, "family": "other", "description": "An eval missed a bar set by `check` (design 0024): a pass rate, a scorer's mean, or a regression against the baseline; `details.failures` lists them."},
    {"code": "invalid_api_key", "layers": ["gateway"], "category": "developer", "retryable": false, "family": null, "http_status": 401, "description": "The gateway needs a valid virtual key."},
    {"code": "model_not_found", "layers": ["gateway"], "category": "developer", "retryable": false, "family": null, "http_status": 404, "description": "No route by that name, or the key may not use it."},
    {"code": "key_rate_limited", "layers": ["gateway"], "category": "transient", "retryable": true, "family": null, "http_status": 429, "description": "The key's requests or tokens per minute are used up; Retry-After says when to try again."},
    {"code": "concurrency_limited", "layers": ["gateway"], "category": "transient", "retryable": true, "family": null, "http_status": 429, "description": "Too many requests are in flight, over the gateway or for this key; Retry-After says when to retry."},
    {"code": "budget_exceeded", "layers": ["gateway"], "category": "policy", "retryable": false, "family": null, "http_status": 429, "description": "The key's token budget is spent for the current day or month (Retry-After: until it ends), or in total."}
  ]
}
