Error Codes
Every failing request answers with the same body, whatever went wrong:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Project 'proj_abc123' not found.",
"hint": "Check the id, and check that the credential can see the project that owns the resource…",
"docs_url": "https://soat.ttoss.dev/docs/error-codes#resource_not_found",
"meta": { "id": "proj_abc123" }
}
}
codeis stable — branch on it, never onmessage.messagedescribes this occurrence and may name ids or fields.hintsays what to do about it, so a caller meeting a code for the first time can act without leaving the response.docs_urladdresses the section for that code on this page.metais present only for codes that carry structured context.
The same catalog is published as JSON at /errors.json and as the
x-error-codes extension of /openapi.json. Both are generated
from the server source, so a client can branch on codes without scraping this
page.
Hints by status class
A code without a hint of its own inherits the one for its HTTP status, so every code answers with something actionable:
| Status | Default hint |
|---|---|
| 400 | Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json. |
| 401 | Authenticate the request: send a valid bearer token or project API key in the Authorization header. |
| 403 | The credential is authenticated but lacks this action, or is scoped to another project. Grant the action in an IAM policy, or use a credential that has it. |
| 404 | Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists. |
| 409 | The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry. |
| 410 | The resource existed and is gone for good. Create a new one; retrying against this id will keep failing. |
| 413 | The payload exceeds the accepted size. Send less in one request, or use the upload flow for large bodies. |
| 422 | The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying. |
| 429 | Back off and retry after the window named by Retry-After. |
| 500 | Retry with backoff. If it persists, the server log carries the detail the response deliberately omits. |
| 501 | Not implemented on this deployment. Retrying will not change the outcome. |
| 502 | An upstream dependency failed rather than this server itself. Retry with backoff and check that dependency. |
| 503 | A capability this operation needs is not configured on this deployment. Configure it — the message names which — rather than retrying. |
All codes
| Code | Status | Meaning |
|---|---|---|
ACTIVITY_INVALID_CURSOR | 400 | The activity feed cursor is malformed or does not decode to a valid (created_at, id) position. |
ACTOR_HAS_MESSAGES | 409 | The actor has linked session messages and cannot be deleted. |
ACTOR_NOT_FOUND | 400 | A referenced actor does not exist. |
AGENT_AND_CHAT_EXCLUSIVE | 400 | An actor cannot have both an agent_id and a chat_id assigned simultaneously. Set one or the other, not both. |
AGENT_HAS_DEPENDENTS | 409 | The agent is referenced by one or more generations, traces, deciders, accepted shares or ingestion rules and cannot be deleted. meta.decider_count names the deciders, which block deletion even under force=true and must be deleted or repointed first; generations, traces, accepted shares (meta.accepted_share_count) and its own project's ingestion rules (meta.ingestion_rule_count) block only until force=true, which revokes the shares and leaves the rules naming a converter that is gone. |
AGENT_NOT_FOUND | 400 | A referenced agent does not exist. |
AI_PROVIDER_ERROR | 502 | The upstream AI provider returned an error (e.g. insufficient credits, rate limit, or the provider is unreachable). |
AI_PROVIDER_HAS_DEPENDENTS | 409 | The AI provider still has dependents. Live references (chats, agents, model-route targets) always block deletion and must be deleted or repointed first. Soft dependents (price overrides, usage records) block only until force=true, which drops the overrides and unlinks usage history. The error meta reports the counts, a sample of offending IDs, and a forcible flag. |
AI_PROVIDER_MISCONFIGURED | 400 | The AI provider record is missing configuration the provider type requires — for example a vertex provider with no Google Cloud project in config.project and no service-account key file linked as its secret, a bedrock/vertex record that links no credential at all on a deployment that does not let a record use the deployment's own, or one whose linked secret holds no usable credential. |
AI_PROVIDER_NOT_FOUND | 400 | A referenced AI provider does not exist. |
API_KEY_PROJECT_SCOPE | 403 | The credential (API key or OAuth token) is bound to one project and the request targets a different one. This project binding is a hard boundary that the owner’s admin role does not lift — admin only relaxes the project create/delete gate, never the per-credential project scope for resource operations. Mint a key scoped to the target project (or an unscoped key) to operate there. The error meta reports scoped_project and requested_project. |
APPROVAL_ALREADY_RESOLVED | 409 | The approval item has already been resolved (approved, rejected, or expired) and cannot be resolved again. |
APPROVAL_EXPIRED | 409 | The approval item has expired and can never execute. Its supporting evidence is stale; a fresh proposal is required. |
APPROVAL_INVALID_EDIT | 400 | The edited arguments supplied on edit-then-approve must be a JSON object, and must satisfy the tool's own parameters schema. |
APPROVAL_NOT_FOUND | 404 | The approval item does not exist or is not accessible. |
APPROVAL_REASON_REQUIRED | 400 | A reason is required when rejecting an approval item. |
BOOTSTRAP_ALREADY_COMPLETED | 409 | The instance already has at least one user, so the one-time bootstrap of the first admin cannot run again. |
CHAIN_NOT_FOUND | 404 | The continuation chain does not exist or is not accessible. |
CHAT_NOT_FOUND | 400 | A referenced chat does not exist. |
CONVERSATION_POSITION_CONFLICT | 409 | Another write took the message's position in the conversation at the same time. Nothing was written: retry the write. |
CONVERSION_TIMEOUT | 500 | Recorded on a document, never returned by a request: its converter deferred with { status: "pending" } and did not call back within CONVERSION_STALL_TIMEOUT_MS (thirty minutes by default), so the next read marked it failed. |
CONVERTER_FAILED | 422 | The ingestion converter (tool or agent) failed to run, an agent converter returned an async deferral (unsupported — agent converters are always awaited inline), or a tool converter returned an async deferral during synchronous ingestion (?wait=true, which cannot wait for a callback). |
CONVERTER_OUTPUT_INVALID | 422 | The ingestion converter returned an unrecognized output shape. Expected a string, { pages: [{ text, page_number }] }, or { status: "pending" }. |
DECIDER_AGENT_NOT_TOOL_LESS | 400 | The agent a decider evaluates with has a tool surface: a tool binding left active by active_tool_ids, or a knowledge_config.write_memory_store_id. A decision has no resume route, and a tool call during evaluation would take a side effect the decision cannot record, so a decider agent carries no tools. Knowledge retrieval stays available. |
DECIDER_TOOL_NOT_CALLABLE | 400 | The tool a decider evaluates with cannot answer a decision. Only http and pipeline tools can: a client tool has no caller to hand the call to, and an mcp or builtin tool needs an action a decider does not carry. A tool whose preset_parameters pin input or questions is refused too, since presets are merged over the call input and would replace the caller's input or the questions. meta.tool_id names the tool, with meta.type or meta.pinned. |
DECISION_ANSWER_INVALID | 502 | Recorded on a decision, never returned by a request: the tool answered outside the contract — not a JSON object with an answers array, an answer missing, repeated or naming no question, a value outside the question's answer space, a probability or confidence outside 0–1, or a field the contract does not define. The message names the answer and the field. |
DECISION_INTERRUPTED | 500 | Recorded on a decision, never returned by a request: the process evaluating it stopped before it settled. The evaluated input is not stored, so the decision cannot be re-run; request a new one. |
DOCUMENT_ALREADY_WITHDRAWN | 409 | The document is already withdrawn, so there is nothing to take out. Restore an earlier version to bring it back. |
DOCUMENT_RELATION_EXISTS | 409 | That relation is already asserted between the two documents. Re-asserting the same edge is the same fact, so there is nothing to add. |
EMBEDDING_NOT_CONFIGURED | 503 | The embedding service is not configured. Set EMBEDDING_PROVIDER and EMBEDDING_MODEL environment variables. |
EVAL_SCORING_FAILED | 500 | Recorded on an eval result, never returned by a request: a scorer could not grade the item's output for a reason no more specific code names. The item is excluded from the aggregates rather than scored 0. |
EXCEPTION_ALREADY_RESOLVED | 409 | The exception item has already been resolved and cannot be acknowledged or resolved again. |
EXCEPTION_NOT_FOUND | 404 | The exception item does not exist or is not accessible. |
EXPRESSION_FAILED | 422 | A JSON Logic expression failed to evaluate: it names an operator the engine does not know, or it ran a throw. The message carries what the expression threw. |
FILE_ALREADY_INGESTED | 409 | The file already backs a Document (a file can only be ingested once). Use POST /documents/{document_id}/ingest to re-process the existing document, or upload a new copy of the file to ingest it under a different path/strategy. |
FILE_DOWNLOAD_URL_NOT_CONFIGURED | 500 | An ingestion rule with file_delivery: download_url requires SOAT_BASE_URL to be set — the URL is fetched by an external converter that cannot resolve a relative or localhost address. |
FILE_HAS_DEPENDENTS | 409 | The file is referenced by one or more records and cannot be deleted. Delete the dependent records first. |
FILE_NOT_FOUND | 400 | The referenced file does not exist. |
FILE_PARSE_FAILED | 400 | The referenced file contains no extractable text. |
FILE_STORAGE_MISCONFIGURED | 500 | The file storage backend is not configured correctly — an unknown FILES_STORAGE_PROVIDER, an unsupported storage type, or a missing required variable (e.g. FILES_STORAGE_DIR for local, FILES_S3_BUCKET for s3). |
FILE_TOO_LARGE_FOR_SYNC | 413 | The file is too large to ingest synchronously. Retry without ?wait=true to run ingestion in the background and poll the document status. |
FORBIDDEN | 403 | The authenticated user does not have permission to perform this action. |
FORCED_TOOL_CHOICE_CANNOT_STOP | 400 | The agent forces tool use on every step (tool_choice: "required", or the { type: "tool" } object form) but declares no has_tool_call stop condition, so no turn it runs could ever end by answering — only by exhausting max_steps. Declare the terminal condition, or drop the forcing to "auto" and force a specific step with step_rules instead. |
FORMATION_DELETE_FAILED | 409 | One or more of the formation's resources could not be deleted, so the stack is left in delete_failed. meta.failures names each blocking resource (its logical id, type, and the underlying error) — the common case is a resource the platform refuses to delete on its own, such as an agent that has generation or trace history. Resolve those, then delete the formation again. |
FORMATION_HANDLER_FAILED | 502 | A resource type registered by the deployment operator delegates its lifecycle to an external HTTP handler, and that handler did not answer successfully — it returned a non-2xx status, timed out, was unreachable, or replied with a body the protocol does not allow (a create with no physical_resource_id, for instance). meta.resource_type and meta.request_type name the type and the operation, and the handler's own message is relayed when it sent one. This is an operator-side failure: the template is not at fault and re-deploying it unchanged will fail the same way until the handler is fixed. |
FORMATION_INVALID_METADATA | 400 | The formation-level metadata field contains a sub/param/ref/ref_attr substitution expression. That field is a static annotation bag and is never resolved; put deploy-time substitutions in the template top-level metadata block instead. |
FORMATION_MISSING_PARAMETERS | 400 | The formation template declares one or more required parameters (no default, not satisfied by use_previous_value) that were not supplied or were provided as an empty string. |
FORMATION_REPLACE_CLEANUP_FAILED | 500 | A deploy replaced a resource — the handler answered a new physical_resource_id — and the superseded resource could not be deleted, so it is still live while the formation's ledger already points at the replacement. The deploy itself succeeded: the desired state is realised, and this is carried on the formation (and on its operation) rather than thrown, so it never appears as a response status. meta.failures names each un-deleted resource as { logical_id, resource_type, physical_resource_id, error }. Each one stays on the formation as pending cleanup and is retried on the next deploy and on teardown; resolve whatever refused the delete and deploy again. |
FORMATION_RESOURCE_TYPE_UNREGISTERED | 409 | The formation holds a resource whose type the deployment no longer registers, so the platform has no handler to act on it. meta.resource_type names the type. |
GENERATION_ALREADY_IN_PROGRESS | 409 | A generation is already in progress for this session. Wait for it to complete before starting a new one. |
GENERATION_CONTENT_UNAVAILABLE | 409 | The generation's content was never stored (the agent or project runs with trace_content_mode 'none') or has since been purged, so the turn cannot be replayed or curated. |
GENERATION_FAILED | 500 | An unexpected error occurred during agent generation. Check the trace for details. |
GENERATION_NOT_AWAITING_TOOL_OUTPUTS | 409 | The generation is not paused on client tool calls: it never paused, or its outputs were already submitted and it resumed. Tool outputs are accepted once per pause. |
GENERATION_NOT_COMPLETED | 409 | The generation has not completed, so it has no finished turn: it cannot be curated into a dataset item, and an eval item whose generation settled in another status (a requires_action pause, for one) is recorded with this code rather than scored. meta.status is the status it settled in. |
GENERATION_NOT_FOUND | 404 | The generation does not exist or is not accessible to the caller. |
GUARDRAIL_HAS_REFERENCES | 409 | The guardrail is still attached to one or more tools, agents, or projects (guardrail_ids) and cannot be deleted. Detach every reference first (requires guardrails:DetachGuardrail). The error meta lists the referencing resources by scope. |
GUARDRAIL_NOT_FOUND | 400 | A guardrail referenced by a guardrail_ids attachment does not exist in the project. |
HOOK_INVALID_JSON | 400 | The inbound hook request body is not valid JSON. |
HOOK_PAYLOAD_TOO_LARGE | 413 | The inbound hook request body exceeds the 1 MiB limit. |
IDEMPOTENCY_KEY_REUSED | 409 | The idempotency key is already claimed by a run, generation, session message or task transition sent from a different request. A key names one request, so a caller that changed the body and kept the key has a bug; send the new request under a new key, or repeat the original body to replay what the key already names. |
INGESTION_CALLBACK_CONFLICT | 409 | The document is no longer awaiting this conversion attempt — it already completed, timed out, or was superseded by a re-ingest. |
INGESTION_CALLBACK_INVALID_TOKEN | 401 | The ingestion-callback token is missing, malformed, expired, or does not match the target document. |
INGESTION_FAILED | 500 | Recorded on a document, never returned by a request: its ingestion failed for a reason no more specific code names. The message carries the failure. |
INGESTION_RULE_GLOB_CONFLICT | 409 | An ingestion rule for this content_type_glob already exists in the project. |
INGESTION_RULE_VALIDATION_FAILED | 400 | The ingestion rule configuration is invalid (e.g. tool_id and agent_id are both set or both missing, the converter tool is a client tool, a soat/mcp converter tool is missing an action, content_type_glob is not a valid MIME type glob, or preset_parameters contains the reserved key "file" or "callback"). |
INGESTION_TIMEOUT | 500 | Recorded on a document, never returned by a request: its ingestion made no progress within INGESTION_STALL_TIMEOUT_MS (five minutes by default), so the next read marked it failed. |
INTERNAL_ERROR | 500 | An unhandled server-side failure. The message is always the constant "Internal Server Error" — the underlying exception is logged, never returned, so this error carries no detail a caller can act on beyond retrying. |
INVALID_CRON_EXPRESSION | 400 | The cron expression is invalid. Triggers require a strict 5-field cron expression evaluated in UTC. |
INVALID_EVENT_PATTERN | 400 | The event pattern is invalid. An event trigger subscribes with '', 'prefix.', or an exact event name, and a pattern in a platform namespace must match a registered event. |
INVALID_OUTPUT_SCHEMA | 400 | The agent output_schema is invalid — it must be a JSON Schema object. |
INVALID_TEMPLATE_TOKEN | 400 | A {{...}} double-curly token was found that is not a {{secret:sec_...}} or {{context:<key>}} reference, or a {{context:<key>}} token was found outside execute.headers / mcp.headers. Double curly braces are reserved for those two reference kinds; use single braces ({param}) for URL path parameters. |
INVALID_TOOL_CONTEXT_KEY | 400 | A tool_context key cannot be used as an HTTP header name. Keys are forwarded verbatim as <prefix><key> headers, where the prefix is the deployment's SOAT_HEADER_PREFIX followed by Context- (X-Soat-Context- by default), so a key may only contain letters, digits and the characters !#$%&'*+-.^_`|~, and two keys must not map to the same header name (header names are case-insensitive). |
LITERAL_CREDENTIAL | 400 | A tool definition carries a credential as a literal value: a credential-named header, a secret field under execute.auth, or a credential-named query parameter in a url. Store it as a secret and write a {{secret:sec_...}} reference (or a {{context:<key>}} reference in a header) instead. |
MCP_TOOL_ERROR | 502 | An mcp-type tool call reached the MCP server and came back a failure: a JSON-RPC error member, or a result the server flagged isError. The error meta carries the tool name and the server URL. Distinct from TOOL_HTTP_ERROR, which is the transport itself refusing the call. |
MEMORY_ALREADY_INVALIDATED | 409 | The memory no longer holds — a retraction or a supersede already invalidated it — so there is nothing to retract. meta.memory_id names it; read it by id to see which. |
MEMORY_RULE_VALIDATION_FAILED | 400 | The memory rule configuration is invalid (e.g. agent_id and tool_id are both set, on is not a supported event, the built-in extractor is bound to conversation_messages.generated, an extractor override is combined with a handler, or source_agent_ids is not an array of agent ids). |
MEMORY_STORE_NOT_FOUND | 400 | A referenced memory store does not exist. |
MISSING_TOOL_CONTEXT_KEY | 400 | A tool header references a {{context:<key>}} token whose key is not present in the tool_context for this call. The call is failed rather than sending the header with an empty value. |
MODEL_LISTING_FAILED | 502 | The provider rejected the model listing request, or answered it with something other than JSON. The provider's own status and message are carried in the error message. |
MODEL_LISTING_UNSUPPORTED | 400 | The AI provider type cannot enumerate the models it can run. Azure lists deployments an operator named rather than models, and Ollama lists whatever was pulled onto that host, so neither answers the question. |
MODEL_NOT_PRICED | 409 | The project runs with require_priced_model and no price-book row covers a billable token component of the model this generation would run on, so the spend it would incur could not be measured. The check runs before the provider is called: nothing is sent, spent or metered. meta.unpriced_rows names each (provider, model, component) to price. An agent bound to a model route is held to every target it may fail over to, not just the first. Price the rows, or clear require_priced_model on the project to accept unmeasurable spend. |
MODEL_ROUTE_HAS_DEPENDENTS | 409 | The model route is referenced by one or more agents, or is a project's default_model_route_id, and cannot be deleted. Repoint or delete the referencing consumers first; the error meta reports the counts and a sample of agent IDs. |
MODEL_ROUTE_NOT_FOUND | 400 | A referenced model route does not exist in the project. |
NAME_CONFLICT | 409 | A resource with this name already exists in the project. Use a different name. |
NOT_IMPLEMENTED | 501 | The request is well-formed but names an option this API version does not implement. |
NO_ACTIVE_RELEASE | 409 | The agent has no active release to promote or abort. Set one with PUT /agents/{agent_id}/release first. |
ON_BEHALF_OF_INVALID | 400 | The On-Behalf-Of header is not an unexpired tool assertion this deployment signed (meta.reason: unverified), or the request is not made with an API key scoped to the project the assertion names (meta.reason: project). |
ORCHESTRATION_CYCLE_DETECTED | 422 | The orchestration graph contains a cycle. Cycles are not supported. |
ORCHESTRATION_DISPATCH_FAILED | 422 | A workflow on_enter dispatch's orchestration run settled in a non-success terminal status (failed, cancelled, or expired). |
ORCHESTRATION_HUMAN_NODE_MISMATCH | 400 | The specified node is not the active human node waiting for input. |
ORCHESTRATION_MAX_ITERATIONS_EXCEEDED | 422 | The orchestration run exceeded the maximum number of iterations. |
ORCHESTRATION_NESTED_RUN_FAILED | 422 | A loop / sub_orchestration child run settled in a non-success terminal status (failed, cancelled, or expired) carrying no code of its own, so the node that started it failed too. A child that carries a code fails its parent under that code instead, so the cause reaches the run a caller reads. Matches ORCHESTRATION_DISPATCH_FAILED, the same rule for a workflow on_enter dispatch's run. |
ORCHESTRATION_NODE_FAILED | 422 | A node in the orchestration run failed to execute. |
ORCHESTRATION_NOT_FOUND | 404 | The orchestration does not exist or is not accessible. |
ORCHESTRATION_POLL_EXHAUSTED | 422 | A poll node reached its attempt limit without its exit condition becoming true (only raised when fail_on_timeout is set). |
ORCHESTRATION_RUN_DEPTH_LIMIT | 409 | Starting the next loop / sub_orchestration child run would nest past the depth bound, so it was refused. This bounds a cycle no intra-graph validator can see — a graph whose sub_orchestration node names itself, directly or through a cycle of two graphs — which the orchestration cycle validator (intra-graph, and excluding loop nodes deliberately) cannot detect. The bound is the smaller of MAX_ORCHESTRATION_RUN_DEPTH (default 10) and the project's max_orchestration_run_depth; the error meta names the depth reached, the limit, and which of the two set it. |
ORCHESTRATION_RUN_NOT_AWAITING_INPUT | 409 | The orchestration run is not awaiting input. |
ORCHESTRATION_RUN_NOT_CANCELLABLE | 409 | The orchestration run is already in a terminal state and cannot be cancelled. |
ORCHESTRATION_RUN_NOT_FOUND | 404 | The orchestration run does not exist or is not accessible. |
ORCHESTRATION_RUN_NOT_PAUSABLE | 409 | The orchestration run has already settled, so there is nothing left to pause. Unlike cancel, a pause keeps the run's last checkpoint and the existing resume route re-drives it from there — so it only applies to a run that is queued, running, sleeping or awaiting input. |
ORCHESTRATION_RUN_PAUSED | 409 | An operator pause is in force on the run, so the requested action was refused. An operator pause has no payload to supply, and a pause standing behind a human or approval node must not be lifted by satisfying it — resume the run first, then submit. |
ORCHESTRATION_VALIDATION_FAILED | 400 | The orchestration graph is invalid (e.g. a dangling edge, a node missing a required field, a cycle, or an input_mapping that references a state key no upstream node writes). |
OUTPUT_SCHEMA_STREAMING_UNSUPPORTED | 400 | Streaming generation does not support output_schema. Set stream to false, or remove output_schema from the agent/request. |
OUTPUT_SCHEMA_VALIDATION_FAILED | 502 | The model returned an object that does not satisfy the agent output_schema (or was not valid JSON at all). Upstream-caused like AI_PROVIDER_ERROR: the request was well-formed, the model output was not. |
PDF_PARSE_FAILED | 400 | The uploaded file could not be parsed as a PDF. Ensure the file is a valid PDF document. |
PIPELINE_DEPTH_EXCEEDED | 422 | The pipeline tool exceeded the maximum nested execution depth (pipelines calling pipelines). |
PIPELINE_INVALID_STEP | 400 | The pipeline tool configuration is invalid (e.g. no steps and no output, a duplicate or malformed step id, a missing tool reference, a forward reference to a later step, or a step that targets a client tool). |
PIPELINE_STEP_FAILED | 422 | A step in the pipeline tool failed to execute. |
PLAYBOOK_AUTHOR_UNAVAILABLE | 409 | The case's playbook version has no author a node can run as: the user or API key that wrote it no longer exists, or it was written with a trigger or OAuth token, which carry their boundary in the token. |
PLAYBOOK_CASE_BUSY | 409 | An upgrade of a case while one of its executions is running: it would settle against nodes the new version may not have. meta.executions names each by id and node. |
PLAYBOOK_CASE_NOT_PAUSED | 409 | The case carries no pause, so there is nothing to resume. |
PLAYBOOK_CASE_PAUSED | 409 | The case is paused, so none of its nodes starts: neither the cascade nor a run of one node. Executions already in flight settle. Resume the case first. |
PLAYBOOK_EXECUTION_FAILED | 422 | A node's executor settled without a value the node can hold as its output: an agent that returned neither text nor an object, a decision or orchestration run that failed, or a task closed without a payload. Recorded on the execution's error, never answered by a request. |
PLAYBOOK_EXECUTION_INTERRUPTED | 500 | Recorded on an execution, never returned by a request: the process running the node's agent, tool or decider stopped before it settled. It is not run again on its own, so a call with side effects never runs twice; run the node again. |
PLAYBOOK_EXECUTOR_FORBIDDEN | 403 | The playbook version's author may not start an executor the definition names. Refused when the version is written, and again when a node runs if the author's permission was removed since. meta names the node and the IAM action. |
PLAYBOOK_EXPORT_FAILED | 422 | Recorded on an export, never returned by a request: the transformation's tool failed, or answered no file. A tool answers a file with execute.response_mode: base64. |
PLAYBOOK_EXPORT_FORMAT_MISMATCH | 422 | Recorded on an export, never returned by a request: the transformation's tool answered a file whose content type is not its format's. meta carries both. |
PLAYBOOK_EXPORT_INTERRUPTED | 500 | Recorded on an export, never returned by a request: the process rendering it stopped before it settled. It is not rendered again on its own; export the node again. |
PLAYBOOK_HAS_CASES | 409 | The playbook has cases and cannot be deleted; a case is the record of what was filled against it. |
PLAYBOOK_INPUT_CONFLICT | 409 | An input write's expected_revision is not the field's current revision: someone wrote it since it was read. meta.conflicts lists each field with both revisions; nothing in the batch was written. |
PLAYBOOK_INPUT_INVALID | 400 | An input write names a node or input the case's playbook version does not declare, an input that reads another node, or a value its input's schema refuses. |
PLAYBOOK_NODE_NOT_CONFIRMABLE | 409 | The node cannot be confirmed: it is not a confirm node, or a required input is unset or reads a node that is not done. meta carries the state, missing and blocked_by. |
PLAYBOOK_NODE_NOT_EXPORTABLE | 409 | An export of the latest revision of a node that is not done: it has no output yet, or its output is stale, running or failed. meta carries the state. Name a revision to export an earlier output. |
PLAYBOOK_NODE_NOT_RUNNABLE | 409 | The node cannot run now: it is a confirm node, a required input is unset or reads a node that is not done, or it is already running or waiting on the task or run it started. meta carries the state, missing and blocked_by. |
PLAYBOOK_SELF_WRITE | 403 | A write into a case from the work it is running: a run-as credential minted for the case, for one of its executions in flight, or for the task or orchestration run one started. An executor's output reaches its case only as the node's output. |
PLAYBOOK_VALIDATION_FAILED | 400 | The playbook definition is invalid: a duplicate node or input, an input reading a missing node or field, an executor on a node that may not have one or missing from one that must, transformations on a non-final node, two transformations of one name on a node, or a cycle. meta.problems lists every one. |
POLICY_NOT_FOUND | 400 | A referenced policy does not exist. |
PROJECT_DEFAULT_ROUTE_INHERITED | 409 | A project's default_model_route_id cannot be cleared while consumers that bind neither an AI provider nor a model route inherit it — clearing it would leave them with no resolvable model. Repointing the default to another route is always allowed; the error meta reports the count and a sample of inheriting resource IDs. |
PROJECT_HAS_DEPENDENTS | 409 | The project has one or more dependent resources (agents, ai providers, tools, etc.) and cannot be deleted. Use force=true to delete dependents as well. |
PROJECT_NOT_PAUSED | 409 | The project carries no pause, so there is nothing to resume. |
PROJECT_PAUSED | 409 | The project is paused, so nothing new starts in it: no generation, chat completion, tool call, orchestration run, eval run, decision or trigger firing, and no resume of a run or task the pause holds. Reads and configuration writes still work. meta carries project_id, paused_at and pause_reason. |
PROMOTION_GATE_UNMET | 409 | The release's promotion_gate has no passing eval run against the canary version. Run the eval with agent_version pinned to the canary, or abort the rollout. |
PUBLIC_SHARES_DISABLED | 403 | A share with grantee: "*" offers the resource to every project, which this deployment does not allow. Name one grantee project, or set SHARES_ALLOW_PUBLIC=true on the server. |
QUEUE_DRIVER_MISCONFIGURED | 500 | The orchestration queue driver is not configured correctly — an unknown ORCHESTRATION_QUEUE_DRIVER, or a missing required variable for the selected driver (e.g. ORCHESTRATION_QUEUE_SQS_QUEUE_URL for sqs). |
QUOTA_CONFLICT | 409 | A quota with the same (project, scope, scope_ref, metric, window) already exists. The all-enforce precedence rule makes duplicates pure redundancy, so a duplicate is rejected instead of stored. |
QUOTA_EXCEEDED | 429 | An enforced quota has been exceeded for the request scope. The response carries a Retry-After header (seconds until the window resets) and a meta block naming the breached quota, metric, limit, window, and reset time. |
QUOTA_STORAGE_EXCEEDED | 409 | An enforced storage_bytes quota is exceeded: the project's stored footprint — files, document chunks (text and vector) and memories — is already over the byte limit, and the write would have added to it. No Retry-After is sent, and the refusal is a 409 rather than a 429, because a stock is not a rate: no window reset clears it and waiting changes nothing. The error meta carries current_bytes (the last daily snapshot plus the request's own delta) beside limit, so the caller knows how much to delete. Raised at the corpus write paths — file upload/create, document create, document ingest and re-ingest, memory create. |
QUOTA_UNENFORCEABLE | 409 | An enforced cost_usd quota with on_unpriced: "block" (the default) cannot be evaluated: the current window holds a pricing blackout — several metered events, none of them priced — so the aggregate is 0 however much was actually spent. The cap refuses the generation rather than waving through spend it cannot measure. No Retry-After is sent — the window resetting changes nothing; configure pricing for the models in use, or set the quota's on_unpriced to "allow" to accept unmeasurable spend explicitly. |
REQUEST_REJECTED | 400 | The HTTP framework rejected the request before it reached a handler — a payload over the size limit, an unsupported method, a malformed request line. The accompanying message carries the specific reason, and the response status is the one the framework chose rather than this default. |
RESERVED_PATH | 400 | The path is under '/.system/', which is reserved for files and documents the platform writes on your behalf. Choose a path outside it. |
RESERVED_TAG_KEY | 400 | The tag key is reserved: keys starting with 'system.' are written by the platform to record which conversation, actor, agent and role a row came from, and cannot be set by a caller. |
RESOURCE_NOT_FOUND | 404 | The requested resource does not exist or is not accessible. |
SECRET_HAS_DEPENDENTS | 409 | The secret is referenced by one or more AI providers and cannot be deleted. Use force=true to delete dependents as well. |
SECRET_NOT_DECRYPTABLE | 500 | A stored trigger or webhook secret could not be decrypted — SECRETS_ENCRYPTION_KEY has changed since it was written. Rotating the secret replaces it; restoring the original key recovers every secret written under it. |
SECRET_NOT_FOUND | 400 | A secret referenced by a {{secret:...}} token does not exist in this project. |
SESSION_CLOSED | 409 | The session is closed and does not accept new messages or generation requests. Open a new session to continue. |
SESSION_EXPIRED | 410 | The session has expired due to inactivity. Open a new session to continue. |
SESSION_NOT_FOUND | 400 | A referenced session does not exist. |
SHARE_CAP_EXCEEDED | 429 | The call reached another project's tool or agent through a share whose cap is spent for the current window, so nothing ran and nothing was metered. The response carries a Retry-After header, and meta names the share, its calls and window, resets_at and retry_after. |
SHARE_IN_USE | 409 | The grantee project still names the shared resource. meta.references lists each of its resources that does; revoking the acceptance leaves them degraded. |
SHARE_REVOKED | 403 | The share was revoked by its publisher, or the publisher revoked this project's acceptance of it. A revoked share accepts nothing and cannot be suspended or resumed; a revoked acceptance refuses a new accept from the same project until the publisher deletes it. |
SINGLE_SESSION_CONFLICT | 409 | An open session already exists for this actor. Use the existing session or close it first. |
SYSTEM_MESSAGE_NOT_ALLOWED | 400 | A role: "system" entry was supplied in messages. System content never travels as a message on any SOAT surface — it goes in the instructions field instead — the same name on every surface (a completion request, a Chat, an Agent). Mirrors the AI SDK, whose allowSystemInMessages defaults to false because a system entry in a caller-supplied array is a prompt-injection vector. |
TASK_AUTOMATION_CHAIN_LIMIT | 409 | The task has run too many machine-driven transitions back-to-back with no outside intervention, and the next one was refused. This bounds a cycle composed across modules — a workflow state dispatching work that transitions the task back into that same state — which neither the orchestration cycle validator (intra-graph) nor the workflow itself (where revisiting states is deliberate) can see. Any move by a person, a plain API key, or an approval resolution resets the chain. The ceiling is TASK_AUTOMATION_CHAIN_LIMIT (default 50). |
TASK_AUTOMATION_PROVENANCE_MISSING | 500 | An automation-caused transition was about to be persisted with no recorded cause: principal_id, generation_id, and orchestration_run_id would all be null. This is a writer bug, not a valid degraded record — the transition is rejected instead of being silently written. |
TASK_GUARD_REJECTED | 400 | The transition guard evaluated to false for this task. |
TASK_MIGRATION_CONFLICT | 409 | The task cannot change workflow version now: it is closed, has a transition pending approval (meta.pending_transition), or has a dispatch in flight whose routing belongs to the version it was dispatched under. |
TASK_MIGRATION_STATE_UNMAPPED | 400 | The task's current state is not declared by the target workflow version and the migration's state_map does not map it. meta.state names it. |
TASK_NOT_FOUND | 404 | The task does not exist or is not accessible. |
TASK_NOT_PAUSABLE | 409 | The task is closed, so there is no automation left to pause. A workflow has no run object, so an operator pause lands on its instance — the task — and suppresses every state dispatch until it is resumed. |
TASK_NOT_PAUSED | 409 | The task carries no operator pause, so there is nothing to resume. Resuming is only how a pause is lifted; a task that is merely idle is advanced by firing a transition. |
TASK_PAYLOAD_INVALID | 400 | The task payload does not satisfy the workflow's payload_schema. |
TASK_STATE_NOT_FOUND | 400 | A task create request's state, or a migration's state_map target, does not name a declared state of the workflow version. meta.state names it. |
TASK_TRANSITION_CONFLICT | 409 | A concurrent change made the requested transition invalid from the current state, or the task is already closed. |
TASK_TRANSITION_NOT_FOUND | 400 | The named transition does not exist in the workflow. |
TEXT_ENCODED_TOOL_CALL | 502 | The model wrote a tool invocation as plain assistant text (e.g. a ```json block containing {"name": "<a bound tool>", "arguments": {…}}) instead of making a structured tool call, so the tool never ran. Upstream-caused like AI_PROVIDER_ERROR: returning that text as the answer would be a silent data-integrity failure, so the generation fails instead. |
TOOL_AUTH_FAILED | 502 | An http-type tool's execute.auth credentials could not be turned into a request credential — malformed service account JSON, an unusable private key, or a token endpoint that rejected the assertion. The error meta carries the token endpoint's status and body when it responded. Distinct from TOOL_HTTP_ERROR, which is the tool's own target rejecting the call. |
TOOL_CALL_FAILED | 500 | Recorded on a generation transcript's tool result, never returned by a request: a tool call made during the turn failed with an error that carries no code of its own, and the model received the failure as the call's result. meta carries the fields the thrown error held (name, an HTTP tool's status, url, body, …). |
TOOL_CALL_NOT_SUPPORTED | 422 | This tool type cannot be invoked server-side. Client tools must be executed by the calling client. |
TOOL_DISPATCH_FAILED | 422 | A tool call was settled by a guardrail before it ran — blocked (class D or a class-B tripwire), or routed to human approval (class C) on a path with nowhere to park. The error meta carries the tool_id and the outcome that settled it. Raised on every dispatch that cannot await sign-off: a direct POST /tools/{tool_id}/call, a pipeline step, a trigger firing a tool target, and a workflow on_enter tool dispatch. Reach the tool through an agent or an orchestration instead when it is approval-gated: both can park on the approval and resume from it. |
TOOL_EGRESS_BLOCKED | 403 | An outbound request the deployment makes to a URL a tenant chose — an http- or mcp-type tool's target, a webhook's url, an AI provider's base_url, a service-account key file's token_uri — names a destination that is not publicly routable: a loopback, private, link-local (cloud metadata), CGNAT or IPv6 ULA address, and the deployment's TOOL_EGRESS_ALLOWED_HOSTS does not list it. Also returned when a hostname resolves to such an address, when a redirect leads to one, when the scheme is not http/https, or when the redirect chain is too long. The error meta carries tool_url and, when known, the offending tool_address. A webhook delivery refused this way is closed rather than retried, with the reason on the delivery row. |
TOOL_HAS_DEPENDENTS | 409 | The tool is the backend of one or more deciders, or another project has accepted a share of it, and it cannot be deleted. meta.decider_count counts the deciders, which block even under force=true: delete them or point them at another backend first. meta.accepted_share_count counts the accepted shares and meta.ingestion_rule_count the ingestion rules of the tool's own project converting with it; both block only until force=true, which revokes the shares and leaves the rules naming a converter that is gone. |
TOOL_HTTP_ERROR | 502 | An http-type tool call returned a non-2xx response. The error meta carries the real upstream status code, response body, URL, and method. |
TOOL_NOT_FOUND | 400 | A referenced tool does not exist. |
TOOL_RESPONSE_TOO_LARGE | 502 | An http-type tool with execute.response_mode: base64 answered with a body over the deployment's ceiling (TOOL_RESPONSE_MAX_BYTES, the file upload ceiling by default). The body is refused while it streams, so nothing was buffered. The error meta carries tool_url and max_bytes. |
TOOL_UNREACHABLE | 502 | An http-type tool call never got a response: the target's host did not resolve, refused or dropped the connection, or timed out. The message names the network failure and the error meta carries the URL and method. Distinct from TOOL_HTTP_ERROR, which is the target answering with a non-2xx status. |
TRIGGER_ACTION_NOT_ALLOWED | 400 | A trigger field is incompatible with its type: action is only valid for tool targets (and required for soat/mcp tools), and cron is required for schedule triggers and rejected otherwise. |
TRIGGER_CAUSATION_LIMIT | 409 | An event trigger refused to extend the causal chain that reached it — it is already in the chain, or the chain has run too deep. |
TRIGGER_CREATOR_UNAVAILABLE | 409 | The trigger creator no longer exists, so the firing identity cannot be resolved. |
TRIGGER_FIRING_ABANDONED | 500 | An event firing was interrupted without reaching a result, repeatedly, and is not attempted again. meta.attempts is how many starts it had. It appears as a firing's recorded error, never as a response: by the time it is written there is nobody left to answer. |
TRIGGER_INPUT_INVALID | 400 | The effective input is invalid for the target (empty agent input, or an orchestration input_schema violation). |
TRIGGER_NOT_ACTIVE | 409 | The trigger is inactive and cannot fire. |
TRIGGER_RECURSION_FORBIDDEN | 403 | A trigger-scoped credential cannot fire a trigger, preventing unbounded trigger→trigger loops. |
TRIGGER_TARGET_NOT_FOUND | 400 | The trigger target does not exist in the project, or its type does not match target_type. |
TRIGGER_TYPE_IMMUTABLE | 400 | A trigger’s type cannot be changed after creation. |
UNAUTHORIZED | 401 | Authentication is required or the token is invalid. |
UNSUPPORTED_FILE_TYPE | 400 | The referenced file has a content type that cannot be ingested. Supported types: application/pdf, text/plain, text/markdown. |
UPLOAD_TOKEN_EXPIRED | 410 | The upload token has expired. Request a new one. |
UPLOAD_TOKEN_NOT_FOUND | 404 | The upload token does not exist. |
UPLOAD_TOKEN_USED | 409 | The upload token has already been used. |
UPLOAD_TOO_LARGE | 413 | The multipart upload exceeds the byte ceiling this deployment accepts (FILE_UPLOAD_MAX_BYTES, 25 MB by default). The request is refused while the body is still streaming, so nothing was stored. Send a smaller file, or ask the operator to raise the ceiling. |
VALIDATION_FAILED | 400 | The request body or query string failed validation. |
VERSION_CONFLICT | 409 | The write named a version other than the one the resource currently holds, or a concurrent write took that version first. meta.current_version is the version in force, and meta.expected_version the one the request named. Nothing was written: re-read the resource, rebase the change on what it now holds, and retry against the version meta.current_version names. |
WORKFLOW_HAS_OPEN_TASKS | 409 | The workflow has one or more open tasks and cannot be deleted. |
WORKFLOW_MIGRATION_NOT_FOUND | 404 | The workflow migration does not exist, or belongs to another workflow. |
WORKFLOW_NOT_FOUND | 404 | The workflow does not exist or is not accessible. |
WORKFLOW_VALIDATION_FAILED | 400 | The workflow definition is invalid (e.g. duplicate state names, not exactly one initial state, a transition referencing an unknown state, a malformed guard, or an on_enter dispatch missing its target). |
WORKFLOW_VERSION_NOT_FOUND | 400 | The workflow has no version with that number. meta.version names it; list the workflow versions to see which exist. |
Reference
ACTIVITY_INVALID_CURSOR
HTTP 400 — The activity feed cursor is malformed or does not decode to a valid (created_at, id) position.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#activity_invalid_cursor
ACTOR_HAS_MESSAGES
HTTP 409 — The actor has linked session messages and cannot be deleted.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#actor_has_messages
ACTOR_NOT_FOUND
HTTP 400 — A referenced actor does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#actor_not_found
AGENT_AND_CHAT_EXCLUSIVE
HTTP 400 — An actor cannot have both an agent_id and a chat_id assigned simultaneously. Set one or the other, not both.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#agent_and_chat_exclusive
AGENT_HAS_DEPENDENTS
HTTP 409 — The agent is referenced by one or more generations, traces, deciders, accepted shares or ingestion rules and cannot be deleted. meta.decider_count names the deciders, which block deletion even under force=true and must be deleted or repointed first; generations, traces, accepted shares (meta.accepted_share_count) and its own project's ingestion rules (meta.ingestion_rule_count) block only until force=true, which revokes the shares and leaves the rules naming a converter that is gone.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#agent_has_dependents
AGENT_NOT_FOUND
HTTP 400 — A referenced agent does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#agent_not_found
AI_PROVIDER_ERROR
HTTP 502 — The upstream AI provider returned an error (e.g. insufficient credits, rate limit, or the provider is unreachable).
What to do: The upstream model provider failed, not this server. Check the provider credential and the account behind it (credits, rate limits, model access), then retry with backoff.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ai_provider_error
AI_PROVIDER_HAS_DEPENDENTS
HTTP 409 — The AI provider still has dependents. Live references (chats, agents, model-route targets) always block deletion and must be deleted or repointed first. Soft dependents (price overrides, usage records) block only until force=true, which drops the overrides and unlinks usage history. The error meta reports the counts, a sample of offending IDs, and a forcible flag.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ai_provider_has_dependents
AI_PROVIDER_MISCONFIGURED
HTTP 400 — The AI provider record is missing configuration the provider type requires — for example a vertex provider with no Google Cloud project in config.project and no service-account key file linked as its secret, a bedrock/vertex record that links no credential at all on a deployment that does not let a record use the deployment's own, or one whose linked secret holds no usable credential.
What to do: The provider record is missing something it needs to make a call — usually the API key secret or the base URL. A bedrock or vertex record linking no credential is this too: it would sign with the deployment's own, which only an operator may allow. Update the AI provider and retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ai_provider_misconfigured
AI_PROVIDER_NOT_FOUND
HTTP 400 — A referenced AI provider does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ai_provider_not_found
API_KEY_PROJECT_SCOPE
HTTP 403 — The credential (API key or OAuth token) is bound to one project and the request targets a different one. This project binding is a hard boundary that the owner’s admin role does not lift — admin only relaxes the project create/delete gate, never the per-credential project scope for resource operations. Mint a key scoped to the target project (or an unscoped key) to operate there. The error meta reports scoped_project and requested_project.
What to do: This API key is scoped to a single project and the request targets another one (or none). Omit project_id to use the key's own project, or use an unscoped credential.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#api_key_project_scope
APPROVAL_ALREADY_RESOLVED
HTTP 409 — The approval item has already been resolved (approved, rejected, or expired) and cannot be resolved again.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#approval_already_resolved
APPROVAL_EXPIRED
HTTP 409 — The approval item has expired and can never execute. Its supporting evidence is stale; a fresh proposal is required.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#approval_expired
APPROVAL_INVALID_EDIT
HTTP 400 — The edited arguments supplied on edit-then-approve must be a JSON object, and must satisfy the tool's own parameters schema.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#approval_invalid_edit
APPROVAL_NOT_FOUND
HTTP 404 — The approval item does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#approval_not_found
APPROVAL_REASON_REQUIRED
HTTP 400 — A reason is required when rejecting an approval item.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#approval_reason_required
BOOTSTRAP_ALREADY_COMPLETED
HTTP 409 — The instance already has at least one user, so the one-time bootstrap of the first admin cannot run again.
What to do: The first admin already exists, so bootstrap is closed for good. Sign in instead, and create further users with POST /api/v1/users.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#bootstrap_already_completed
CHAIN_NOT_FOUND
HTTP 404 — The continuation chain does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#chain_not_found
CHAT_NOT_FOUND
HTTP 400 — A referenced chat does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#chat_not_found
CONVERSATION_POSITION_CONFLICT
HTTP 409 — Another write took the message's position in the conversation at the same time. Nothing was written: retry the write.
What to do: Retry the write unchanged: nothing was stored, and the retry resolves its position again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#conversation_position_conflict
CONVERSION_TIMEOUT
HTTP 500 — Recorded on a document, never returned by a request: its converter deferred with { status: "pending" } and did not call back within CONVERSION_STALL_TIMEOUT_MS (thirty minutes by default), so the next read marked it failed.
What to do: Check that the converter calls back to the document's callback URL, then ingest the document again. Raise CONVERSION_STALL_TIMEOUT_MS for a converter that legitimately takes longer.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#conversion_timeout
CONVERTER_FAILED
HTTP 422 — The ingestion converter (tool or agent) failed to run, an agent converter returned an async deferral (unsupported — agent converters are always awaited inline), or a tool converter returned an async deferral during synchronous ingestion (?wait=true, which cannot wait for a callback).
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#converter_failed
CONVERTER_OUTPUT_INVALID
HTTP 422 — The ingestion converter returned an unrecognized output shape. Expected a string, { pages: [{ text, page_number }] }, or { status: "pending" }.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#converter_output_invalid
DECIDER_AGENT_NOT_TOOL_LESS
HTTP 400 — The agent a decider evaluates with has a tool surface: a tool binding left active by active_tool_ids, or a knowledge_config.write_memory_store_id. A decision has no resume route, and a tool call during evaluation would take a side effect the decision cannot record, so a decider agent carries no tools. Knowledge retrieval stays available.
What to do: Point the decider at an agent with no tool_bindings and no knowledge_config.write_memory_store_id, or remove them from this one with PATCH /api/v1/agents/{agent_id}. The agent's instructions and knowledge retrieval remain the place for domain context.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#decider_agent_not_tool_less
DECIDER_TOOL_NOT_CALLABLE
HTTP 400 — The tool a decider evaluates with cannot answer a decision. Only http and pipeline tools can: a client tool has no caller to hand the call to, and an mcp or builtin tool needs an action a decider does not carry. A tool whose preset_parameters pin input or questions is refused too, since presets are merged over the call input and would replace the caller's input or the questions. meta.tool_id names the tool, with meta.type or meta.pinned.
What to do: Point the decider at an http or pipeline tool with no input or questions in its preset_parameters. Reach an mcp or builtin tool through a pipeline step, which names its own action.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#decider_tool_not_callable
DECISION_ANSWER_INVALID
HTTP 502 — Recorded on a decision, never returned by a request: the tool answered outside the contract — not a JSON object with an answers array, an answer missing, repeated or naming no question, a value outside the question's answer space, a probability or confidence outside 0–1, or a field the contract does not define. The message names the answer and the field.
What to do: Make the tool answer { answers: [{ name, probability | choice | score, probabilities?, confidence? }] }, one per question, as OpenAI's Decisions API does; map an engine's own fields with the tool's output_mapping or a pipeline tool's output. Then request a new decision.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#decision_answer_invalid
DECISION_INTERRUPTED
HTTP 500 — Recorded on a decision, never returned by a request: the process evaluating it stopped before it settled. The evaluated input is not stored, so the decision cannot be re-run; request a new one.
What to do: Request a new decision with POST /api/v1/deciders/{decider_id}/decisions or POST /api/v1/decisions; nothing about the interrupted one can be resumed.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#decision_interrupted
DOCUMENT_ALREADY_WITHDRAWN
HTTP 409 — The document is already withdrawn, so there is nothing to take out. Restore an earlier version to bring it back.
What to do: The document is already out of every default read. To bring it back, restore the version before the withdrawal.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#document_already_withdrawn
DOCUMENT_RELATION_EXISTS
HTTP 409 — That relation is already asserted between the two documents. Re-asserting the same edge is the same fact, so there is nothing to add.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#document_relation_exists
EMBEDDING_NOT_CONFIGURED
HTTP 503 — The embedding service is not configured. Set EMBEDDING_PROVIDER and EMBEDDING_MODEL environment variables.
What to do: Knowledge search needs an embedding model. Configure one on the project or the AI provider before ingesting or searching.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#embedding_not_configured
EVAL_SCORING_FAILED
HTTP 500 — Recorded on an eval result, never returned by a request: a scorer could not grade the item's output for a reason no more specific code names. The item is excluded from the aggregates rather than scored 0.
What to do: Read the message for the scorer's failure, fix the scorer's configuration, and run the eval again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#eval_scoring_failed
EXCEPTION_ALREADY_RESOLVED
HTTP 409 — The exception item has already been resolved and cannot be acknowledged or resolved again.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#exception_already_resolved
EXCEPTION_NOT_FOUND
HTTP 404 — The exception item does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#exception_not_found
EXPRESSION_FAILED
HTTP 422 — A JSON Logic expression failed to evaluate: it names an operator the engine does not know, or it ran a throw. The message carries what the expression threw.
What to do: Fix the JSON Logic expression the message names: an unknown operator, or a throw the expression reached with the data it was given.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#expression_failed
FILE_ALREADY_INGESTED
HTTP 409 — The file already backs a Document (a file can only be ingested once). Use POST /documents/{document_id}/ingest to re-process the existing document, or upload a new copy of the file to ingest it under a different path/strategy.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_already_ingested
FILE_DOWNLOAD_URL_NOT_CONFIGURED
HTTP 500 — An ingestion rule with file_delivery: download_url requires SOAT_BASE_URL to be set — the URL is fetched by an external converter that cannot resolve a relative or localhost address.
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_download_url_not_configured
FILE_HAS_DEPENDENTS
HTTP 409 — The file is referenced by one or more records and cannot be deleted. Delete the dependent records first.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_has_dependents
FILE_NOT_FOUND
HTTP 400 — The referenced file does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_not_found
FILE_PARSE_FAILED
HTTP 400 — The referenced file contains no extractable text.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_parse_failed
FILE_STORAGE_MISCONFIGURED
HTTP 500 — The file storage backend is not configured correctly — an unknown FILES_STORAGE_PROVIDER, an unsupported storage type, or a missing required variable (e.g. FILES_STORAGE_DIR for local, FILES_S3_BUCKET for s3).
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_storage_misconfigured
FILE_TOO_LARGE_FOR_SYNC
HTTP 413 — The file is too large to ingest synchronously. Retry without ?wait=true to run ingestion in the background and poll the document status.
What to do: The file is too large to parse inside the request. Call the same operation without wait=true and poll the handle it returns.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#file_too_large_for_sync
FORBIDDEN
HTTP 403 — The authenticated user does not have permission to perform this action.
What to do: The credential authenticated but is not allowed this action. Attach an IAM policy granting the action to the caller, or use a credential that has it.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#forbidden
FORCED_TOOL_CHOICE_CANNOT_STOP
HTTP 400 — The agent forces tool use on every step (tool_choice: "required", or the { type: "tool" } object form) but declares no has_tool_call stop condition, so no turn it runs could ever end by answering — only by exhausting max_steps. Declare the terminal condition, or drop the forcing to "auto" and force a specific step with step_rules instead.
What to do: Add { "type": "has_tool_call", "tool_name": "<your done tool>" } to stop_conditions, or set tool_choice to "auto" and force the step you actually care about with step_rules. max_chain_generations does not satisfy this — it bounds a chain, it never ends a turn.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#forced_tool_choice_cannot_stop
FORMATION_DELETE_FAILED
HTTP 409 — One or more of the formation's resources could not be deleted, so the stack is left in delete_failed. meta.failures names each blocking resource (its logical id, type, and the underlying error) — the common case is a resource the platform refuses to delete on its own, such as an agent that has generation or trace history. Resolve those, then delete the formation again.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_delete_failed
FORMATION_HANDLER_FAILED
HTTP 502 — A resource type registered by the deployment operator delegates its lifecycle to an external HTTP handler, and that handler did not answer successfully — it returned a non-2xx status, timed out, was unreachable, or replied with a body the protocol does not allow (a create with no physical_resource_id, for instance). meta.resource_type and meta.request_type name the type and the operation, and the handler's own message is relayed when it sent one. This is an operator-side failure: the template is not at fault and re-deploying it unchanged will fail the same way until the handler is fixed.
What to do: A resource type this deployment registered delegates to an external handler, and that handler did not answer. The template is not at fault, so re-deploying it unchanged will fail identically — the deployment operator has to fix the handler (meta.resource_type names the type, meta.request_type the operation it failed on).
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_handler_failed
FORMATION_INVALID_METADATA
HTTP 400 — The formation-level metadata field contains a sub/param/ref/ref_attr substitution expression. That field is a static annotation bag and is never resolved; put deploy-time substitutions in the template top-level metadata block instead.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_invalid_metadata
FORMATION_MISSING_PARAMETERS
HTTP 400 — The formation template declares one or more required parameters (no default, not satisfied by use_previous_value) that were not supplied or were provided as an empty string.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_missing_parameters
FORMATION_REPLACE_CLEANUP_FAILED
HTTP 500 — A deploy replaced a resource — the handler answered a new physical_resource_id — and the superseded resource could not be deleted, so it is still live while the formation's ledger already points at the replacement. The deploy itself succeeded: the desired state is realised, and this is carried on the formation (and on its operation) rather than thrown, so it never appears as a response status. meta.failures names each un-deleted resource as { logical_id, resource_type, physical_resource_id, error }. Each one stays on the formation as pending cleanup and is retried on the next deploy and on teardown; resolve whatever refused the delete and deploy again.
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_replace_cleanup_failed
FORMATION_RESOURCE_TYPE_UNREGISTERED
HTTP 409 — The formation holds a resource whose type the deployment no longer registers, so the platform has no handler to act on it. meta.resource_type names the type.
What to do: Register the resource type again so its handler can act on the resource, or remove the resource outside the formation.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#formation_resource_type_unregistered
GENERATION_ALREADY_IN_PROGRESS
HTTP 409 — A generation is already in progress for this session. Wait for it to complete before starting a new one.
What to do: A generation is already running for this target. Poll it to completion, or cancel it, before starting another.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_already_in_progress
GENERATION_CONTENT_UNAVAILABLE
HTTP 409 — The generation's content was never stored (the agent or project runs with trace_content_mode 'none') or has since been purged, so the turn cannot be replayed or curated.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_content_unavailable
GENERATION_FAILED
HTTP 500 — An unexpected error occurred during agent generation. Check the trace for details.
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_failed
GENERATION_NOT_AWAITING_TOOL_OUTPUTS
HTTP 409 — The generation is not paused on client tool calls: it never paused, or its outputs were already submitted and it resumed. Tool outputs are accepted once per pause.
What to do: Read the generation for its current status. A generation that pauses again answers requires_action with new tool call ids; submit outputs for those.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_not_awaiting_tool_outputs
GENERATION_NOT_COMPLETED
HTTP 409 — The generation has not completed, so it has no finished turn: it cannot be curated into a dataset item, and an eval item whose generation settled in another status (a requires_action pause, for one) is recorded with this code rather than scored. meta.status is the status it settled in.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_not_completed
GENERATION_NOT_FOUND
HTTP 404 — The generation does not exist or is not accessible to the caller.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#generation_not_found
GUARDRAIL_HAS_REFERENCES
HTTP 409 — The guardrail is still attached to one or more tools, agents, or projects (guardrail_ids) and cannot be deleted. Detach every reference first (requires guardrails:DetachGuardrail). The error meta lists the referencing resources by scope.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#guardrail_has_references
GUARDRAIL_NOT_FOUND
HTTP 400 — A guardrail referenced by a guardrail_ids attachment does not exist in the project.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#guardrail_not_found
HOOK_INVALID_JSON
HTTP 400 — The inbound hook request body is not valid JSON.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#hook_invalid_json
HOOK_PAYLOAD_TOO_LARGE
HTTP 413 — The inbound hook request body exceeds the 1 MiB limit.
What to do: The payload exceeds the accepted size. Send less in one request, or use the upload flow for large bodies.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#hook_payload_too_large
IDEMPOTENCY_KEY_REUSED
HTTP 409 — The idempotency key is already claimed by a run, generation, session message or task transition sent from a different request. A key names one request, so a caller that changed the body and kept the key has a bug; send the new request under a new key, or repeat the original body to replay what the key already names.
What to do: Send the changed request under a new idempotency_key; repeating the original body under this key replays what it already names.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#idempotency_key_reused
INGESTION_CALLBACK_CONFLICT
HTTP 409 — The document is no longer awaiting this conversion attempt — it already completed, timed out, or was superseded by a re-ingest.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_callback_conflict
INGESTION_CALLBACK_INVALID_TOKEN
HTTP 401 — The ingestion-callback token is missing, malformed, expired, or does not match the target document.
What to do: Authenticate the request: send a valid bearer token or project API key in the Authorization header.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_callback_invalid_token
INGESTION_FAILED
HTTP 500 — Recorded on a document, never returned by a request: its ingestion failed for a reason no more specific code names. The message carries the failure.
What to do: Read the message for the cause, fix the file or its ingestion rule, and ingest the document again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_failed
INGESTION_RULE_GLOB_CONFLICT
HTTP 409 — An ingestion rule for this content_type_glob already exists in the project.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_rule_glob_conflict
INGESTION_RULE_VALIDATION_FAILED
HTTP 400 — The ingestion rule configuration is invalid (e.g. tool_id and agent_id are both set or both missing, the converter tool is a client tool, a soat/mcp converter tool is missing an action, content_type_glob is not a valid MIME type glob, or preset_parameters contains the reserved key "file" or "callback").
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_rule_validation_failed
INGESTION_TIMEOUT
HTTP 500 — Recorded on a document, never returned by a request: its ingestion made no progress within INGESTION_STALL_TIMEOUT_MS (five minutes by default), so the next read marked it failed.
What to do: Ingest the document again. A large file or a busy worker can outlast the window; raise INGESTION_STALL_TIMEOUT_MS when it does so repeatedly.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#ingestion_timeout
INTERNAL_ERROR
HTTP 500 — An unhandled server-side failure. The message is always the constant "Internal Server Error" — the underlying exception is logged, never returned, so this error carries no detail a caller can act on beyond retrying.
What to do: Retry with backoff. The underlying exception is logged server-side and deliberately never forwarded, so the response carries nothing further to act on.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#internal_error
INVALID_CRON_EXPRESSION
HTTP 400 — The cron expression is invalid. Triggers require a strict 5-field cron expression evaluated in UTC.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#invalid_cron_expression
INVALID_EVENT_PATTERN
HTTP 400 — The event pattern is invalid. An event trigger subscribes with '', 'prefix.', or an exact event name, and a pattern in a platform namespace must match a registered event.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#invalid_event_pattern
INVALID_OUTPUT_SCHEMA
HTTP 400 — The agent output_schema is invalid — it must be a JSON Schema object.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#invalid_output_schema
INVALID_TEMPLATE_TOKEN
HTTP 400 — A {{...}} double-curly token was found that is not a {{secret:sec_...}} or {{context:<key>}} reference, or a {{context:<key>}} token was found outside execute.headers / mcp.headers. Double curly braces are reserved for those two reference kinds; use single braces ({param}) for URL path parameters.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#invalid_template_token
INVALID_TOOL_CONTEXT_KEY
HTTP 400 — A tool_context key cannot be used as an HTTP header name. Keys are forwarded verbatim as <prefix><key> headers, where the prefix is the deployment's SOAT_HEADER_PREFIX followed by Context- (X-Soat-Context- by default), so a key may only contain letters, digits and the characters !#$%&'*+-.^_`|~, and two keys must not map to the same header name (header names are case-insensitive).
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#invalid_tool_context_key
LITERAL_CREDENTIAL
HTTP 400 — A tool definition carries a credential as a literal value: a credential-named header, a secret field under execute.auth, or a credential-named query parameter in a url. Store it as a secret and write a {{secret:sec_...}} reference (or a {{context:<key>}} reference in a header) instead.
What to do: Create the value with POST /secrets and write {{secret:<secret_id>}} where the literal was (for example Authorization: Bearer {{secret:sec_...}}); meta.paths names every field to change.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#literal_credential
MCP_TOOL_ERROR
HTTP 502 — An mcp-type tool call reached the MCP server and came back a failure: a JSON-RPC error member, or a result the server flagged isError. The error meta carries the tool name and the server URL. Distinct from TOOL_HTTP_ERROR, which is the transport itself refusing the call.
What to do: The MCP server answered the call with a failure rather than a result — meta.mcp_tool names the tool and meta.mcp_url the server. The message carries what the server said; fix the arguments or the server-side condition it names, then retry. The call reached the server, so this is not a connectivity or allowlist problem.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#mcp_tool_error
MEMORY_ALREADY_INVALIDATED
HTTP 409 — The memory no longer holds — a retraction or a supersede already invalidated it — so there is nothing to retract. meta.memory_id names it; read it by id to see which.
What to do: The fact is already out of every read. Read the memory by id to see whether a supersede replaced it, and retract the memory that replaced it if that is the one that no longer holds.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#memory_already_invalidated
MEMORY_RULE_VALIDATION_FAILED
HTTP 400 — The memory rule configuration is invalid (e.g. agent_id and tool_id are both set, on is not a supported event, the built-in extractor is bound to conversation_messages.generated, an extractor override is combined with a handler, or source_agent_ids is not an array of agent ids).
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#memory_rule_validation_failed
MEMORY_STORE_NOT_FOUND
HTTP 400 — A referenced memory store does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#memory_store_not_found
MISSING_TOOL_CONTEXT_KEY
HTTP 400 — A tool header references a {{context:<key>}} token whose key is not present in the tool_context for this call. The call is failed rather than sending the header with an empty value.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#missing_tool_context_key
MODEL_LISTING_FAILED
HTTP 502 — The provider rejected the model listing request, or answered it with something other than JSON. The provider's own status and message are carried in the error message.
What to do: An upstream dependency failed rather than this server itself. Retry with backoff and check that dependency.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#model_listing_failed
MODEL_LISTING_UNSUPPORTED
HTTP 400 — The AI provider type cannot enumerate the models it can run. Azure lists deployments an operator named rather than models, and Ollama lists whatever was pulled onto that host, so neither answers the question.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#model_listing_unsupported
MODEL_NOT_PRICED
HTTP 409 — The project runs with require_priced_model and no price-book row covers a billable token component of the model this generation would run on, so the spend it would incur could not be measured. The check runs before the provider is called: nothing is sent, spent or metered. meta.unpriced_rows names each (provider, model, component) to price. An agent bound to a model route is held to every target it may fail over to, not just the first. Price the rows, or clear require_priced_model on the project to accept unmeasurable spend.
What to do: Add price book rows for every (provider, model, component) in meta.unpriced_rows — globally with PUT /api/v1/usage/prices, or for one provider instance with PUT /api/v1/ai-providers/{ai_provider_id}/prices. To run unpriced models again, set require_priced_model to false with PATCH /api/v1/projects/{project_id}.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#model_not_priced
MODEL_ROUTE_HAS_DEPENDENTS
HTTP 409 — The model route is referenced by one or more agents, or is a project's default_model_route_id, and cannot be deleted. Repoint or delete the referencing consumers first; the error meta reports the counts and a sample of agent IDs.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#model_route_has_dependents
MODEL_ROUTE_NOT_FOUND
HTTP 400 — A referenced model route does not exist in the project.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#model_route_not_found
NAME_CONFLICT
HTTP 409 — A resource with this name already exists in the project. Use a different name.
What to do: A resource with this name already exists in the project. Pick another name, or update the existing resource instead of creating a second one.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#name_conflict
NOT_IMPLEMENTED
HTTP 501 — The request is well-formed but names an option this API version does not implement.
What to do: The operation exists in the contract but is not implemented on this deployment. Retrying will not change the outcome.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#not_implemented
NO_ACTIVE_RELEASE
HTTP 409 — The agent has no active release to promote or abort. Set one with PUT /agents/{agent_id}/release first.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#no_active_release
ON_BEHALF_OF_INVALID
HTTP 400 — The On-Behalf-Of header is not an unexpired tool assertion this deployment signed (meta.reason: unverified), or the request is not made with an API key scoped to the project the assertion names (meta.reason: project).
What to do: Forward the tool assertion of the call being served unchanged, within its lifetime, with the API key of the project it names; or drop the header to write as the key.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#on_behalf_of_invalid
ORCHESTRATION_CYCLE_DETECTED
HTTP 422 — The orchestration graph contains a cycle. Cycles are not supported.
What to do: The graph has a cycle, so no topological order exists. Break the cycle named in meta — a loop belongs in a workflow, not a DAG.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_cycle_detected
ORCHESTRATION_DISPATCH_FAILED
HTTP 422 — A workflow on_enter dispatch's orchestration run settled in a non-success terminal status (failed, cancelled, or expired).
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_dispatch_failed
ORCHESTRATION_HUMAN_NODE_MISMATCH
HTTP 400 — The specified node is not the active human node waiting for input.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_human_node_mismatch
ORCHESTRATION_MAX_ITERATIONS_EXCEEDED
HTTP 422 — The orchestration run exceeded the maximum number of iterations.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_max_iterations_exceeded
ORCHESTRATION_NESTED_RUN_FAILED
HTTP 422 — A loop / sub_orchestration child run settled in a non-success terminal status (failed, cancelled, or expired) carrying no code of its own, so the node that started it failed too. A child that carries a code fails its parent under that code instead, so the cause reaches the run a caller reads. Matches ORCHESTRATION_DISPATCH_FAILED, the same rule for a workflow on_enter dispatch's run.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_nested_run_failed
ORCHESTRATION_NODE_FAILED
HTTP 422 — A node in the orchestration run failed to execute.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_node_failed
ORCHESTRATION_NOT_FOUND
HTTP 404 — The orchestration does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_not_found
ORCHESTRATION_POLL_EXHAUSTED
HTTP 422 — A poll node reached its attempt limit without its exit condition becoming true (only raised when fail_on_timeout is set).
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_poll_exhausted
ORCHESTRATION_RUN_DEPTH_LIMIT
HTTP 409 — Starting the next loop / sub_orchestration child run would nest past the depth bound, so it was refused. This bounds a cycle no intra-graph validator can see — a graph whose sub_orchestration node names itself, directly or through a cycle of two graphs — which the orchestration cycle validator (intra-graph, and excluding loop nodes deliberately) cannot detect. The bound is the smaller of MAX_ORCHESTRATION_RUN_DEPTH (default 10) and the project's max_orchestration_run_depth; the error meta names the depth reached, the limit, and which of the two set it.
What to do: Nesting is recursing: follow parent_orchestration_run_id up from the failed run to find the sub_orchestration (or loop) node that names a graph already in the chain, and repoint it. If the composition is legitimately this deep, raise the project bound with PATCH /api/v1/projects/{project_id} (max_orchestration_run_depth) — meta.limit_source says whether the project or the deployment set the number that refused it.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_depth_limit
ORCHESTRATION_RUN_NOT_AWAITING_INPUT
HTTP 409 — The orchestration run is not awaiting input.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_not_awaiting_input
ORCHESTRATION_RUN_NOT_CANCELLABLE
HTTP 409 — The orchestration run is already in a terminal state and cannot be cancelled.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_not_cancellable
ORCHESTRATION_RUN_NOT_FOUND
HTTP 404 — The orchestration run does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_not_found
ORCHESTRATION_RUN_NOT_PAUSABLE
HTTP 409 — The orchestration run has already settled, so there is nothing left to pause. Unlike cancel, a pause keeps the run's last checkpoint and the existing resume route re-drives it from there — so it only applies to a run that is queued, running, sleeping or awaiting input.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_not_pausable
ORCHESTRATION_RUN_PAUSED
HTTP 409 — An operator pause is in force on the run, so the requested action was refused. An operator pause has no payload to supply, and a pause standing behind a human or approval node must not be lifted by satisfying it — resume the run first, then submit.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_run_paused
ORCHESTRATION_VALIDATION_FAILED
HTTP 400 — The orchestration graph is invalid (e.g. a dangling edge, a node missing a required field, a cycle, or an input_mapping that references a state key no upstream node writes).
What to do: The orchestration graph is not runnable as declared. meta names the failing node or edge; POST /api/v1/orchestrations/{orchestration_id}/validate reports every problem at once.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#orchestration_validation_failed
OUTPUT_SCHEMA_STREAMING_UNSUPPORTED
HTTP 400 — Streaming generation does not support output_schema. Set stream to false, or remove output_schema from the agent/request.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#output_schema_streaming_unsupported
OUTPUT_SCHEMA_VALIDATION_FAILED
HTTP 502 — The model returned an object that does not satisfy the agent output_schema (or was not valid JSON at all). Upstream-caused like AI_PROVIDER_ERROR: the request was well-formed, the model output was not.
What to do: The model answered and the answer does not satisfy output_schema — the violated field is named in the message. Retrying the same request bills a second call to reach the same answer; loosen the schema, or make the instructions name the shape the model keeps missing.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#output_schema_validation_failed
PDF_PARSE_FAILED
HTTP 400 — The uploaded file could not be parsed as a PDF. Ensure the file is a valid PDF document.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#pdf_parse_failed
PIPELINE_DEPTH_EXCEEDED
HTTP 422 — The pipeline tool exceeded the maximum nested execution depth (pipelines calling pipelines).
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#pipeline_depth_exceeded
PIPELINE_INVALID_STEP
HTTP 400 — The pipeline tool configuration is invalid (e.g. no steps and no output, a duplicate or malformed step id, a missing tool reference, a forward reference to a later step, or a step that targets a client tool).
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#pipeline_invalid_step
PIPELINE_STEP_FAILED
HTTP 422 — A step in the pipeline tool failed to execute.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#pipeline_step_failed
PLAYBOOK_AUTHOR_UNAVAILABLE
HTTP 409 — The case's playbook version has no author a node can run as: the user or API key that wrote it no longer exists, or it was written with a trigger or OAuth token, which carry their boundary in the token.
What to do: Write the playbook's definition again as a user or API key that can start its executors; cases created on that version run as them.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_author_unavailable
PLAYBOOK_CASE_BUSY
HTTP 409 — An upgrade of a case while one of its executions is running: it would settle against nodes the new version may not have. meta.executions names each by id and node.
What to do: Wait for the executions meta.executions names to settle — close a workflow task, or let an orchestration run end — then retry the upgrade.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_case_busy
PLAYBOOK_CASE_NOT_PAUSED
HTTP 409 — The case carries no pause, so there is nothing to resume.
What to do: Nothing to do: the case is already running.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_case_not_paused
PLAYBOOK_CASE_PAUSED
HTTP 409 — The case is paused, so none of its nodes starts: neither the cascade nor a run of one node. Executions already in flight settle. Resume the case first.
What to do: Resume the case with POST /api/v1/playbook-cases/{case_id}/resume, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_case_paused
PLAYBOOK_EXECUTION_FAILED
HTTP 422 — A node's executor settled without a value the node can hold as its output: an agent that returned neither text nor an object, a decision or orchestration run that failed, or a task closed without a payload. Recorded on the execution's error, never answered by a request.
What to do: Read the execution's error and the run it names in run_id, fix the executor, and run the node again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_execution_failed
PLAYBOOK_EXECUTION_INTERRUPTED
HTTP 500 — Recorded on an execution, never returned by a request: the process running the node's agent, tool or decider stopped before it settled. It is not run again on its own, so a call with side effects never runs twice; run the node again.
What to do: Run the node again with POST /api/v1/playbook-cases/{case_id}/nodes/{node}/run once whatever its executor called is known not to need undoing.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_execution_interrupted
PLAYBOOK_EXECUTOR_FORBIDDEN
HTTP 403 — The playbook version's author may not start an executor the definition names. Refused when the version is written, and again when a node runs if the author's permission was removed since. meta names the node and the IAM action.
What to do: Grant the author the start action meta.action names on the executor, or have someone who holds it write the playbook's definition, which makes them the author of the new version.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_executor_forbidden
PLAYBOOK_EXPORT_FAILED
HTTP 422 — Recorded on an export, never returned by a request: the transformation's tool failed, or answered no file. A tool answers a file with execute.response_mode: base64.
What to do: Read the export's error, make the transformation's tool answer a file (execute.response_mode: base64), and export the node again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_export_failed
PLAYBOOK_EXPORT_FORMAT_MISMATCH
HTTP 422 — Recorded on an export, never returned by a request: the transformation's tool answered a file whose content type is not its format's. meta carries both.
What to do: Make the tool answer the content type of the transformation's format, or declare the format it produces, and export the node again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_export_format_mismatch
PLAYBOOK_EXPORT_INTERRUPTED
HTTP 500 — Recorded on an export, never returned by a request: the process rendering it stopped before it settled. It is not rendered again on its own; export the node again.
What to do: Export the node again with POST /api/v1/playbook-cases/{case_id}/nodes/{node}/exports.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_export_interrupted
PLAYBOOK_HAS_CASES
HTTP 409 — The playbook has cases and cannot be deleted; a case is the record of what was filled against it.
What to do: Delete the playbook’s cases first, or keep the playbook and stop creating cases from it.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_has_cases
PLAYBOOK_INPUT_CONFLICT
HTTP 409 — An input write's expected_revision is not the field's current revision: someone wrote it since it was read. meta.conflicts lists each field with both revisions; nothing in the batch was written.
What to do: Re-read the case, decide each conflicting field against its current value, and retry with the revisions meta.conflicts names.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_input_conflict
PLAYBOOK_INPUT_INVALID
HTTP 400 — An input write names a node or input the case's playbook version does not declare, an input that reads another node, or a value its input's schema refuses.
What to do: Read the case's nodes for the inputs its version declares and which of them are filled by a person; correct the value to its input's schema.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_input_invalid
PLAYBOOK_NODE_NOT_CONFIRMABLE
HTTP 409 — The node cannot be confirmed: it is not a confirm node, or a required input is unset or reads a node that is not done. meta carries the state, missing and blocked_by.
What to do: Read the case: confirm only a confirm node, after filling what missing names and once the nodes blocked_by names are done.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_node_not_confirmable
PLAYBOOK_NODE_NOT_EXPORTABLE
HTTP 409 — An export of the latest revision of a node that is not done: it has no output yet, or its output is stale, running or failed. meta carries the state. Name a revision to export an earlier output.
What to do: Wait for the node to settle, or run it again if it failed; to export an earlier output, name its revision from the case's entries.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_node_not_exportable
PLAYBOOK_NODE_NOT_RUNNABLE
HTTP 409 — The node cannot run now: it is a confirm node, a required input is unset or reads a node that is not done, or it is already running or waiting on the task or run it started. meta carries the state, missing and blocked_by.
What to do: Read the case: fill what missing names, wait for the nodes blocked_by names, or confirm a confirm node instead of running it. A running node settles on its own; a waiting one settles when the task or run its run names closes or ends.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_node_not_runnable
PLAYBOOK_SELF_WRITE
HTTP 403 — A write into a case from the work it is running: a run-as credential minted for the case, for one of its executions in flight, or for the task or orchestration run one started. An executor's output reaches its case only as the node's output.
What to do: Return the value as the executor's result instead: it becomes the node's output, which the nodes reading it take as input.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_self_write
PLAYBOOK_VALIDATION_FAILED
HTTP 400 — The playbook definition is invalid: a duplicate node or input, an input reading a missing node or field, an executor on a node that may not have one or missing from one that must, transformations on a non-final node, two transformations of one name on a node, or a cycle. meta.problems lists every one.
What to do: Fix every problem meta.problems lists and retry. POST /api/v1/playbooks/validate answers the same check without writing.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#playbook_validation_failed
POLICY_NOT_FOUND
HTTP 400 — A referenced policy does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#policy_not_found
PROJECT_DEFAULT_ROUTE_INHERITED
HTTP 409 — A project's default_model_route_id cannot be cleared while consumers that bind neither an AI provider nor a model route inherit it — clearing it would leave them with no resolvable model. Repointing the default to another route is always allowed; the error meta reports the count and a sample of inheriting resource IDs.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#project_default_route_inherited
PROJECT_HAS_DEPENDENTS
HTTP 409 — The project has one or more dependent resources (agents, ai providers, tools, etc.) and cannot be deleted. Use force=true to delete dependents as well.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#project_has_dependents
PROJECT_NOT_PAUSED
HTTP 409 — The project carries no pause, so there is nothing to resume.
What to do: Nothing to do: the project is already running. Read paused_at on GET /api/v1/projects/{project_id} before resuming.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#project_not_paused
PROJECT_PAUSED
HTTP 409 — The project is paused, so nothing new starts in it: no generation, chat completion, tool call, orchestration run, eval run, decision or trigger firing, and no resume of a run or task the pause holds. Reads and configuration writes still work. meta carries project_id, paused_at and pause_reason.
What to do: Resume the project with POST /api/v1/projects/{project_id}/resume once whatever paused it is resolved; meta.pause_reason says why it was paused. Nothing is queued while paused — retry the request after resuming.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#project_paused
PROMOTION_GATE_UNMET
HTTP 409 — The release's promotion_gate has no passing eval run against the canary version. Run the eval with agent_version pinned to the canary, or abort the rollout.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#promotion_gate_unmet
PUBLIC_SHARES_DISABLED
HTTP 403 — A share with grantee: "*" offers the resource to every project, which this deployment does not allow. Name one grantee project, or set SHARES_ALLOW_PUBLIC=true on the server.
What to do: Create the share with a project id as grantee, or ask the operator to set SHARES_ALLOW_PUBLIC=true.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#public_shares_disabled
QUEUE_DRIVER_MISCONFIGURED
HTTP 500 — The orchestration queue driver is not configured correctly — an unknown ORCHESTRATION_QUEUE_DRIVER, or a missing required variable for the selected driver (e.g. ORCHESTRATION_QUEUE_SQS_QUEUE_URL for sqs).
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#queue_driver_misconfigured
QUOTA_CONFLICT
HTTP 409 — A quota with the same (project, scope, scope_ref, metric, window) already exists. The all-enforce precedence rule makes duplicates pure redundancy, so a duplicate is rejected instead of stored.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#quota_conflict
QUOTA_EXCEEDED
HTTP 429 — An enforced quota has been exceeded for the request scope. The response carries a Retry-After header (seconds until the window resets) and a meta block naming the breached quota, metric, limit, window, and reset time.
What to do: Wait until the window resets — Retry-After (seconds) and meta.resets_at both carry the time — or raise the quota with PATCH /api/v1/quotas/{quota_id}.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#quota_exceeded
QUOTA_STORAGE_EXCEEDED
HTTP 409 — An enforced storage_bytes quota is exceeded: the project's stored footprint — files, document chunks (text and vector) and memories — is already over the byte limit, and the write would have added to it. No Retry-After is sent, and the refusal is a 409 rather than a 429, because a stock is not a rate: no window reset clears it and waiting changes nothing. The error meta carries current_bytes (the last daily snapshot plus the request's own delta) beside limit, so the caller knows how much to delete. Raised at the corpus write paths — file upload/create, document create, document ingest and re-ingest, memory create.
What to do: Delete stored content — files, documents, or memories — until the project is back under the cap; meta.current_bytes and meta.limit say by how much. Waiting does not help: a storage quota caps a stored total, not a rate. Alternatively raise the cap with PATCH /api/v1/quotas/{quota_id}, or switch it to monitor mode to stop blocking while you measure.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#quota_storage_exceeded
QUOTA_UNENFORCEABLE
HTTP 409 — An enforced cost_usd quota with on_unpriced: "block" (the default) cannot be evaluated: the current window holds a pricing blackout — several metered events, none of them priced — so the aggregate is 0 however much was actually spent. The cap refuses the generation rather than waving through spend it cannot measure. No Retry-After is sent — the window resetting changes nothing; configure pricing for the models in use, or set the quota's on_unpriced to "allow" to accept unmeasurable spend explicitly.
What to do: Add price book entries covering the models this project runs (POST /api/v1/prices), so the cost quota has something to aggregate. To accept unmeasurable spend instead, set on_unpriced: "allow" on the quota with PATCH /api/v1/quotas/{quota_id} (or switch it to monitor mode to stop blocking entirely).
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#quota_unenforceable
REQUEST_REJECTED
HTTP 400 — The HTTP framework rejected the request before it reached a handler — a payload over the size limit, an unsupported method, a malformed request line. The accompanying message carries the specific reason, and the response status is the one the framework chose rather than this default.
What to do: The HTTP layer rejected the request before it reached a route — method, content type, or size. The message names the reason.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#request_rejected
RESERVED_PATH
HTTP 400 — The path is under '/.system/', which is reserved for files and documents the platform writes on your behalf. Choose a path outside it.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#reserved_path
RESERVED_TAG_KEY
HTTP 400 — The tag key is reserved: keys starting with 'system.' are written by the platform to record which conversation, actor, agent and role a row came from, and cannot be set by a caller.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#reserved_tag_key
RESOURCE_NOT_FOUND
HTTP 404 — The requested resource does not exist or is not accessible.
What to do: Check the id, and check that the credential can see the project that owns the resource — a resource in another project is indistinguishable from one that does not exist. List the collection to confirm.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#resource_not_found
SECRET_HAS_DEPENDENTS
HTTP 409 — The secret is referenced by one or more AI providers and cannot be deleted. Use force=true to delete dependents as well.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#secret_has_dependents
SECRET_NOT_DECRYPTABLE
HTTP 500 — A stored trigger or webhook secret could not be decrypted — SECRETS_ENCRYPTION_KEY has changed since it was written. Rotating the secret replaces it; restoring the original key recovers every secret written under it.
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#secret_not_decryptable
SECRET_NOT_FOUND
HTTP 400 — A secret referenced by a {{secret:...}} token does not exist in this project.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#secret_not_found
SESSION_CLOSED
HTTP 409 — The session is closed and does not accept new messages or generation requests. Open a new session to continue.
What to do: The session is closed and accepts no further messages. Open a new one, or fork this one to keep its history.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#session_closed
SESSION_EXPIRED
HTTP 410 — The session has expired due to inactivity. Open a new session to continue.
What to do: Open a new session; an expired one cannot be revived. Raise the agent's session TTL if this happens sooner than the workload needs.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#session_expired
SESSION_NOT_FOUND
HTTP 400 — A referenced session does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#session_not_found
SHARE_CAP_EXCEEDED
HTTP 429 — The call reached another project's tool or agent through a share whose cap is spent for the current window, so nothing ran and nothing was metered. The response carries a Retry-After header, and meta names the share, its calls and window, resets_at and retry_after.
What to do: Wait retry_after seconds for the share's window to reset, or ask the publisher to raise its cap.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#share_cap_exceeded
SHARE_IN_USE
HTTP 409 — The grantee project still names the shared resource. meta.references lists each of its resources that does; revoking the acceptance leaves them degraded.
What to do: Repoint or remove the resources meta.references lists, or pass force=true to revoke anyway and let them degrade.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#share_in_use
SHARE_REVOKED
HTTP 403 — The share was revoked by its publisher, or the publisher revoked this project's acceptance of it. A revoked share accepts nothing and cannot be suspended or resumed; a revoked acceptance refuses a new accept from the same project until the publisher deletes it.
What to do: A revoked share cannot be used again: ask the publisher for a new share. If the publisher revoked only your acceptance, it must delete that acceptance before you can accept again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#share_revoked
SINGLE_SESSION_CONFLICT
HTTP 409 — An open session already exists for this actor. Use the existing session or close it first.
What to do: The agent allows one open session per actor. Reuse the session named in meta.session_id, or close it first.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#single_session_conflict
SYSTEM_MESSAGE_NOT_ALLOWED
HTTP 400 — A role: "system" entry was supplied in messages. System content never travels as a message on any SOAT surface — it goes in the instructions field instead — the same name on every surface (a completion request, a Chat, an Agent). Mirrors the AI SDK, whose allowSystemInMessages defaults to false because a system entry in a caller-supplied array is a prompt-injection vector.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#system_message_not_allowed
TASK_AUTOMATION_CHAIN_LIMIT
HTTP 409 — The task has run too many machine-driven transitions back-to-back with no outside intervention, and the next one was refused. This bounds a cycle composed across modules — a workflow state dispatching work that transitions the task back into that same state — which neither the orchestration cycle validator (intra-graph) nor the workflow itself (where revisiting states is deliberate) can see. Any move by a person, a plain API key, or an approval resolution resets the chain. The ceiling is TASK_AUTOMATION_CHAIN_LIMIT (default 50).
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_automation_chain_limit
TASK_AUTOMATION_PROVENANCE_MISSING
HTTP 500 — An automation-caused transition was about to be persisted with no recorded cause: principal_id, generation_id, and orchestration_run_id would all be null. This is a writer bug, not a valid degraded record — the transition is rejected instead of being silently written.
What to do: Retry with backoff. If it persists, the server log carries the detail the response deliberately omits.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_automation_provenance_missing
TASK_GUARD_REJECTED
HTTP 400 — The transition guard evaluated to false for this task.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_guard_rejected
TASK_MIGRATION_CONFLICT
HTTP 409 — The task cannot change workflow version now: it is closed, has a transition pending approval (meta.pending_transition), or has a dispatch in flight whose routing belongs to the version it was dispatched under.
What to do: Let the dispatch settle or resolve the pending approval, then migrate again; a closed task stays on its version.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_migration_conflict
TASK_MIGRATION_STATE_UNMAPPED
HTTP 400 — The task's current state is not declared by the target workflow version and the migration's state_map does not map it. meta.state names it.
What to do: Add state_map: { "<meta.state>": "<a state the target version declares>" } to the migration.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_migration_state_unmapped
TASK_NOT_FOUND
HTTP 404 — The task does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_not_found
TASK_NOT_PAUSABLE
HTTP 409 — The task is closed, so there is no automation left to pause. A workflow has no run object, so an operator pause lands on its instance — the task — and suppresses every state dispatch until it is resumed.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_not_pausable
TASK_NOT_PAUSED
HTTP 409 — The task carries no operator pause, so there is nothing to resume. Resuming is only how a pause is lifted; a task that is merely idle is advanced by firing a transition.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_not_paused
TASK_PAYLOAD_INVALID
HTTP 400 — The task payload does not satisfy the workflow's payload_schema.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_payload_invalid
TASK_STATE_NOT_FOUND
HTTP 400 — A task create request's state, or a migration's state_map target, does not name a declared state of the workflow version. meta.state names it.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_state_not_found
TASK_TRANSITION_CONFLICT
HTTP 409 — A concurrent change made the requested transition invalid from the current state, or the task is already closed.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_transition_conflict
TASK_TRANSITION_NOT_FOUND
HTTP 400 — The named transition does not exist in the workflow.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#task_transition_not_found
TEXT_ENCODED_TOOL_CALL
HTTP 502 — The model wrote a tool invocation as plain assistant text (e.g. a ```json block containing {"name": "<a bound tool>", "arguments": {…}}) instead of making a structured tool call, so the tool never ran. Upstream-caused like AI_PROVIDER_ERROR: returning that text as the answer would be a silent data-integrity failure, so the generation fails instead.
What to do: An upstream dependency failed rather than this server itself. Retry with backoff and check that dependency.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#text_encoded_tool_call
TOOL_AUTH_FAILED
HTTP 502 — An http-type tool's execute.auth credentials could not be turned into a request credential — malformed service account JSON, an unusable private key, or a token endpoint that rejected the assertion. The error meta carries the token endpoint's status and body when it responded. Distinct from TOOL_HTTP_ERROR, which is the tool's own target rejecting the call.
What to do: An upstream dependency failed rather than this server itself. Retry with backoff and check that dependency.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_auth_failed
TOOL_CALL_FAILED
HTTP 500 — Recorded on a generation transcript's tool result, never returned by a request: a tool call made during the turn failed with an error that carries no code of its own, and the model received the failure as the call's result. meta carries the fields the thrown error held (name, an HTTP tool's status, url, body, …).
What to do: Read meta for what the tool raised. The turn went on with the failure as the call result, so fix the tool or its target for the next turn.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_call_failed
TOOL_CALL_NOT_SUPPORTED
HTTP 422 — This tool type cannot be invoked server-side. Client tools must be executed by the calling client.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_call_not_supported
TOOL_DISPATCH_FAILED
HTTP 422 — A tool call was settled by a guardrail before it ran — blocked (class D or a class-B tripwire), or routed to human approval (class C) on a path with nowhere to park. The error meta carries the tool_id and the outcome that settled it. Raised on every dispatch that cannot await sign-off: a direct POST /tools/{tool_id}/call, a pipeline step, a trigger firing a tool target, and a workflow on_enter tool dispatch. Reach the tool through an agent or an orchestration instead when it is approval-gated: both can park on the approval and resume from it.
What to do: The request was accepted and then could not be carried out. The failure is in the work, not the request shape — read meta and the trace for which step failed before retrying.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_dispatch_failed
TOOL_EGRESS_BLOCKED
HTTP 403 — An outbound request the deployment makes to a URL a tenant chose — an http- or mcp-type tool's target, a webhook's url, an AI provider's base_url, a service-account key file's token_uri — names a destination that is not publicly routable: a loopback, private, link-local (cloud metadata), CGNAT or IPv6 ULA address, and the deployment's TOOL_EGRESS_ALLOWED_HOSTS does not list it. Also returned when a hostname resolves to such an address, when a redirect leads to one, when the scheme is not http/https, or when the redirect chain is too long. The error meta carries tool_url and, when known, the offending tool_address. A webhook delivery refused this way is closed rather than retried, with the reason on the delivery row.
What to do: The deployment only makes outbound requests to publicly routable addresses, and this destination is not one — meta.tool_url names it and meta.tool_address the address it resolved to. Point the tool, webhook or provider base_url at a public host, or have the operator list this destination in TOOL_EGRESS_ALLOWED_HOSTS. It is not a permissions problem: no policy grants it.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_egress_blocked
TOOL_HAS_DEPENDENTS
HTTP 409 — The tool is the backend of one or more deciders, or another project has accepted a share of it, and it cannot be deleted. meta.decider_count counts the deciders, which block even under force=true: delete them or point them at another backend first. meta.accepted_share_count counts the accepted shares and meta.ingestion_rule_count the ingestion rules of the tool's own project converting with it; both block only until force=true, which revokes the shares and leaves the rules naming a converter that is gone.
What to do: Delete the deciders that name the tool, or point them at another backend with PATCH /api/v1/deciders/{decider_id}, then delete the tool. Accepted shares (meta.accepted_share_count) are revoked by retrying with force=true; ingestion rules (meta.ingestion_rule_count) are repointed with PATCH /api/v1/ingestion-rules/{ingestion_rule_id}, or left without a converter by force=true.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_has_dependents
TOOL_HTTP_ERROR
HTTP 502 — An http-type tool call returned a non-2xx response. The error meta carries the real upstream status code, response body, URL, and method.
What to do: An upstream dependency failed rather than this server itself. Retry with backoff and check that dependency.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_http_error
TOOL_NOT_FOUND
HTTP 400 — A referenced tool does not exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_not_found
TOOL_RESPONSE_TOO_LARGE
HTTP 502 — An http-type tool with execute.response_mode: base64 answered with a body over the deployment's ceiling (TOOL_RESPONSE_MAX_BYTES, the file upload ceiling by default). The body is refused while it streams, so nothing was buffered. The error meta carries tool_url and max_bytes.
What to do: The target's file is over this deployment's ceiling (meta.max_bytes). Fetch a smaller file, or ask the operator to raise TOOL_RESPONSE_MAX_BYTES.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_response_too_large
TOOL_UNREACHABLE
HTTP 502 — An http-type tool call never got a response: the target's host did not resolve, refused or dropped the connection, or timed out. The message names the network failure and the error meta carries the URL and method. Distinct from TOOL_HTTP_ERROR, which is the target answering with a non-2xx status.
What to do: Check the tool's URL and that its host is reachable from the server; the message names the network failure. A timeout or a dropped connection may succeed on retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#tool_unreachable
TRIGGER_ACTION_NOT_ALLOWED
HTTP 400 — A trigger field is incompatible with its type: action is only valid for tool targets (and required for soat/mcp tools), and cron is required for schedule triggers and rejected otherwise.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_action_not_allowed
TRIGGER_CAUSATION_LIMIT
HTTP 409 — An event trigger refused to extend the causal chain that reached it — it is already in the chain, or the chain has run too deep.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_causation_limit
TRIGGER_CREATOR_UNAVAILABLE
HTTP 409 — The trigger creator no longer exists, so the firing identity cannot be resolved.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_creator_unavailable
TRIGGER_FIRING_ABANDONED
HTTP 500 — An event firing was interrupted without reaching a result, repeatedly, and is not attempted again. meta.attempts is how many starts it had. It appears as a firing's recorded error, never as a response: by the time it is written there is nobody left to answer.
What to do: The dispatch did not survive long enough to record an outcome. Check the target named by the trigger for work that ends the process, then re-fire the trigger manually.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_firing_abandoned
TRIGGER_INPUT_INVALID
HTTP 400 — The effective input is invalid for the target (empty agent input, or an orchestration input_schema violation).
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_input_invalid
TRIGGER_NOT_ACTIVE
HTTP 409 — The trigger is inactive and cannot fire.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_not_active
TRIGGER_RECURSION_FORBIDDEN
HTTP 403 — A trigger-scoped credential cannot fire a trigger, preventing unbounded trigger→trigger loops.
What to do: The credential is authenticated but lacks this action, or is scoped to another project. Grant the action in an IAM policy, or use a credential that has it.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_recursion_forbidden
TRIGGER_TARGET_NOT_FOUND
HTTP 400 — The trigger target does not exist in the project, or its type does not match target_type.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_target_not_found
TRIGGER_TYPE_IMMUTABLE
HTTP 400 — A trigger’s type cannot be changed after creation.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#trigger_type_immutable
UNAUTHORIZED
HTTP 401 — Authentication is required or the token is invalid.
What to do: Send Authorization: Bearer <token> — a user token from POST /api/v1/users/login, a project API key (sk_…), or an OAuth access token. Discovery metadata for the OAuth flow is at /.well-known/oauth-authorization-server.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#unauthorized
UNSUPPORTED_FILE_TYPE
HTTP 400 — The referenced file has a content type that cannot be ingested. Supported types: application/pdf, text/plain, text/markdown.
What to do: The file's media type has no parser on this deployment. Convert it to a supported type, or add an ingestion rule that does.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#unsupported_file_type
UPLOAD_TOKEN_EXPIRED
HTTP 410 — The upload token has expired. Request a new one.
What to do: Request a fresh upload token; tokens are single-use and short-lived by design.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#upload_token_expired
UPLOAD_TOKEN_NOT_FOUND
HTTP 404 — The upload token does not exist.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#upload_token_not_found
UPLOAD_TOKEN_USED
HTTP 409 — The upload token has already been used.
What to do: This upload token has already been redeemed. Request a new one for another upload.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#upload_token_used
UPLOAD_TOO_LARGE
HTTP 413 — The multipart upload exceeds the byte ceiling this deployment accepts (FILE_UPLOAD_MAX_BYTES, 25 MB by default). The request is refused while the body is still streaming, so nothing was stored. Send a smaller file, or ask the operator to raise the ceiling.
What to do: The upload is over this deployment's byte ceiling. Send a smaller file, or ask the operator to raise FILE_UPLOAD_MAX_BYTES.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#upload_too_large
VALIDATION_FAILED
HTTP 400 — The request body or query string failed validation.
What to do: Fix the request and retry. Unknown fields are rejected outright, so compare the payload against the operation in /openapi.json; meta names the offending field when the check can identify one, and for an unknown query parameter lists unknown_query_parameters and accepted_query_parameters.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#validation_failed
VERSION_CONFLICT
HTTP 409 — The write named a version other than the one the resource currently holds, or a concurrent write took that version first. meta.current_version is the version in force, and meta.expected_version the one the request named. Nothing was written: re-read the resource, rebase the change on what it now holds, and retry against the version meta.current_version names.
What to do: Re-read the resource, re-apply the change on top of the version meta.current_version names, and retry with that version. A retry of the same body against the same stale version conflicts again.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#version_conflict
WORKFLOW_HAS_OPEN_TASKS
HTTP 409 — The workflow has one or more open tasks and cannot be deleted.
What to do: The request is well-formed but the resource is not in a state that allows it. Resolve the conflicting state named in the message, then retry.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#workflow_has_open_tasks
WORKFLOW_MIGRATION_NOT_FOUND
HTTP 404 — The workflow migration does not exist, or belongs to another workflow.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#workflow_migration_not_found
WORKFLOW_NOT_FOUND
HTTP 404 — The workflow does not exist or is not accessible.
What to do: Check the id and the project the credential can see, then retry. Listing the collection confirms whether the resource exists.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#workflow_not_found
WORKFLOW_VALIDATION_FAILED
HTTP 400 — The workflow definition is invalid (e.g. duplicate state names, not exactly one initial state, a transition referencing an unknown state, a malformed guard, or an on_enter dispatch missing its target).
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#workflow_validation_failed
WORKFLOW_VERSION_NOT_FOUND
HTTP 400 — The workflow has no version with that number. meta.version names it; list the workflow versions to see which exist.
What to do: Fix the request and retry — the payload, parameters, or referenced ids are invalid. Compare the request against the operation in /openapi.json.
Reached by docs_url: https://soat.ttoss.dev/docs/error-codes#workflow_version_not_found