# Errors reference

> Every error answers one body. Branch on `code`, never on the message; show the person `message` when it helps; and send us `request_id` when it is our side.

## The body

- `error` (`object`): The error.
  - `type` (`string`): The kind of error, one per status: `invalid_request`, `authentication`, `permission`, `not_found`, `conflict`, `rate_limited`, `cap_reached`, `upstream` or `internal`. Limits: one of 9 values.
  - `code` (`string`): What went wrong, exactly: branch on this. The errors reference lists every code, with what to do. Example: `user_not_found`.
  - `message` (`string`): What went wrong, in a sentence a person can read. It may change: never branch on it.
  - `param` (`string`, may be absent): The field the error is about, as a path such as `tools.0.input`; absent when it is about no one field.
  - `names` (`string[]`, may be absent): With `secret_missing`: the company secrets to store before applying the file again.
  - `deployments` (`string[]`, may be absent): With `mcp_server_shared`: the other deployments that run the MCP server the file would change.
  - `partial` (`object`, may be absent): With `timeout` on a browser fetch: what the page had sent before the time ran out.
  - `retry_after_ms` (`integer`, may be absent): With `browser_busy`: how long to wait before trying again, in ms.
  - `request_id` (`string`): This request's id: send it to Vestra when the error is on our side.

```json
{
  "error": {
    "type": "not_found",
    "code": "user_not_found",
    "message": "No such user.",
    "request_id": "req_8f2k"
  }
}
```

## 400: The request is not one the API takes

- `answer_mismatch`: The answer's shape is not one this interaction takes. What to do: Send the body its type takes: an approval `approve`, a question `answers`, a call to your tool `output` or `error`.
- `answers_mismatch`: The answers do not match the questions. What to do: Send exactly one answer for each question, in the order they were asked.
- `answers_not_expected`: This interaction takes a yes or a no, not answers. What to do: Send `{ "approve": true }` or `{ "approve": false }`.
- `answers_required`: This question waits for answers. What to do: Send `answers`, one for each question, or `{ "approve": false }` to decline.
- `app_not_connectable`: This app cannot be connected yet. What to do: Pick another app from `GET /v1/apps`.
- `auth_twice`: An MCP tool in the file gives both `auth` and an Authorization header. What to do: Keep one of the two.
- `binary_file`: A skill holds a file that is not text. What to do: Skills take text files only: leave out images and other binaries.
- `body_too_large`: The request body, or a context inside it, is larger than this route takes. What to do: Send less, or upload the data as a file with `POST /v1/files` and name it.
- `bundle_too_large`: A skill is larger than 5 MB. What to do: Make the skill smaller: keep what Bash reads, and leave out the rest.
- `cap_above_maximum`: A cap is above what is allowed: the company cap above its ceiling, or a user cap above the company cap. What to do: Lower the cap, or ask Vestra to raise the ceiling.
- `connect_app_instead`: This interaction is an app to connect, and it is not answered with a body. What to do: Connect the app with `POST /v1/connections`: the interaction settles itself once the app is connected.
- `conversation_required`: The MCP tool needs a conversation and none was named. What to do: Name the conversation to act on.
- `conversation_user_mismatch`: The conversation belongs to a different user than the one named. What to do: Name the conversation of the same user, or leave the user out.
- `decision_not_expected`: A hand-off answer was sent to something that is not a hand-off, or the other way round. What to do: Send `done`, `could_not` or `not_now` to a hand-off only, and the body its type takes to the others.
- `deployment_required`: This call needs a deployment and none was named. What to do: Name the deployment by its name or its `dep_` id.
- `describe_or_answer`: The MCP `init` tool needs a description to start, or a deployment, a conversation and an answer to go on. What to do: Start with `describe`, then answer with `deployment`, `conversation` and `answer`.
- `document_too_large`: A notebook document is larger than 512 KB of text. What to do: Split it into several documents.
- `duplicate_name`: The file names two tools, tests or routines the same. What to do: Give each its own name.
- `duplicate_path`: Two files of a skill share one path. What to do: Give each file its own path.
- `file_required`: The upload has no file in it. What to do: Send the file as multipart/form-data, in a field named `file`.
- `file_too_large` (also 413): A file is larger than its route takes: a file of a skill over 1 MB, or an upload over its limit. What to do: Send a smaller file.
- `install_ability_instead`: Bash asked to install an ability, and that is not approved through the API. What to do: Add the skill or MCP server to your deployment instead; this interaction can only be declined.
- `invalid_conversation_id`: `conversation_id` is not a conversation id. What to do: Send a `conv_` id, as the API gave it.
- `invalid_cursor`: The cursor is not one this API gave. What to do: Send the `next_cursor` of the page before, unchanged.
- `invalid_deployment`: That is not a deployment id or name. What to do: Send a `dep_` id, or the name, with an optional `@version`.
- `invalid_deployment_id`: `deployment_id` is not a deployment id. What to do: Send a `dep_` id, as the API gave it.
- `invalid_filename`: The file name is not one plain name. What to do: Use one name with no slashes or control characters, at most 255 bytes.
- `invalid_frontmatter`: A skill's SKILL.md frontmatter is not valid YAML. What to do: Fix the YAML between the two `---` lines at the top of SKILL.md.
- `invalid_idempotency_key`: The Idempotency-Key header is empty or too long. What to do: Send 1 to 255 characters.
- `invalid_json`: The request body is not valid JSON. What to do: Send JSON, with `Content-Type: application/json`.
- `invalid_language`: That is not a language tag. What to do: Use `auto`, or a tag such as `en`, `hi` or `pt-BR`.
- `invalid_last_event_id`: `Last-Event-ID` or `after` is not a whole number. What to do: Send the `seq` of the last event you saw, or 0 for the start.
- `invalid_limit`: `limit` is not a whole number from 1 to 100. What to do: Send a `limit` from 1 to 100, or leave it out.
- `invalid_manifest`: The deployment's settings do not fit together: the message and `param` say where. What to do: Fix the field `param` names and send it again.
- `invalid_memory`: The memory is not one Bash can keep. What to do: Send `text` of 1 to 1,000 characters, and optionally a `kind`: identity, relationship, commitment, preference, project_state or other.
- `invalid_mode`: The `Vestra-Mode` header is not live or test. What to do: Send `live` or `test`.
- `invalid_org_header`: The `Vestra-Org` header is not an org id. What to do: Send your org id, like `org_...`.
- `invalid_origin`: An allowed origin is not an https origin. What to do: Write it as `https://app.example.com`, with no path.
- `invalid_query`: A query field is not valid: `param` names it. What to do: Fix that field, or leave it out.
- `invalid_repo`: A repository in the file is not an address Bash can clone. What to do: Write `github.com/owner/repo`, or its https URL.
- `invalid_request`: The request is not valid: the message and `param` say which field and why. What to do: Fix that field and send it again. The route says what each field takes.
- `invalid_schedule`: The schedule is not one a routine can keep: an unknown time zone, or a cron that does not read. What to do: Use an IANA time zone such as `Asia/Kolkata`, and a cron such as `0 7 * * *`, or write `every` in plain words.
- `invalid_secret_name`: That is not a secret name. What to do: Use capitals, digits and underscores, starting with a capital: `PNR_API_KEY`.
- `invalid_skill`: A skill's SKILL.md does not meet the skill format. What to do: Fix what the message names, and upload the skill again.
- `invalid_task_id`: `task_id` is not a task id. What to do: Send a `task_` id, as the API gave it.
- `invalid_tool_input`: A function tool's `input` is neither shorthand nor JSON Schema. What to do: Write each argument as a type such as `string` or `integer?`, or give JSON Schema with `type: object`.
- `invalid_trigger`: A routine's `filters` are not ones it can match. What to do: Fix the filter the message names.
- `invalid_user_timezone`: The user's own time zone is not an IANA name, so `every` cannot be read in it. What to do: Send `timezone`, or fix the user with `PATCH /v1/users/{user_id}`.
- `invalid_window`: `from` is after `to`, or one of them is not a date. What to do: Send dates like `2026-10-01`, with `from` on or before `to`.
- `invalid_zip`: The skill upload is not a zip archive that reads. What to do: Zip the skill folder again and upload it.
- `mcp_server_not_listed`: A tool policy names an MCP server the version does not list. What to do: Add the server to `mcp_servers`, or drop its policy.
- `memory_endpoint_required`: Memory kept on your side needs an endpoint for Bash to ask. What to do: Send `memory_endpoint` with `memory_mode: customer`.
- `message_required`: There is nothing to send: no message, attachment or tool result. What to do: Send a `message`, an attachment or tool results.
- `multipart_required`: The upload is not multipart/form-data. What to do: Send the file as multipart/form-data, in a field named `file`.
- `name_mismatch`: The file's `name` and the name in the path differ. What to do: Make them the same.
- `no_output_schema`: This work was not asked for structured output, so it has no result to read. What to do: Ask with `output_schema` when you want a result to read.
- `nonce_mismatch`: An approval that moves money or acts for the user needs its nonce, and it was missing or wrong. What to do: Read the interaction and send its `nonce` with `approve: true`.
- `not_a_schedule_routine`: This routine runs on a call, so it has no schedule to change. What to do: Change its filters, or make a new routine that runs on a schedule.
- `not_a_trigger_routine`: This routine runs on a schedule, so it takes no payload and has no filters. What to do: Run it with `POST /v1/routines/{routine_id}/run`.
- `notebook_full`: The notebook is full: too many documents, or more than 5 MB of text. What to do: Delete documents, or start another notebook.
- `oauth_not_available`: OAuth for company MCP servers is not available yet. What to do: Send headers with `${user_secret:NAME}` for each user's own credentials.
- `output_schema_invalid`: `output_schema` is not a schema Bash can follow. What to do: Send a JSON Schema object; the message says what is wrong with it.
- `output_schema_too_large`: `output_schema` is larger than 32 KB. What to do: Send a smaller schema.
- `path_not_resolved`: The file names a folder or file on your computer, and HTTP cannot read it. What to do: Deploy with the CLI or an SDK: they upload it and send what it became.
- `payload_too_large`: A routine's trigger payload is larger than 4 KB. What to do: Send less, or put the data in a file and name it.
- `present_user_required`: This approval moves money or acts for the user, and the user must be there to say yes. What to do: Approve it only while the user is in your app, and send `present_user: true`.
- `query_required`: A memory search needs a question. What to do: Send `q`, 1 to 500 characters.
- `raw_secret_in_header`: An MCP header holds a raw credential. What to do: Store it with `PUT /v1/secrets/{name}` and write `${secret:NAME}` in the header.
- `return_url_not_allowed`: `return_url` is not one of your allowed return URLs. What to do: Add it with `PATCH /v1/org`, or send one that is allowed.
- `schedule_too_frequent`: A routine runs at most once every 15 minutes. What to do: Choose a schedule that runs less often.
- `schedule_unreadable`: `every` does not read as a schedule. What to do: Write it like `weekday at 08:00`, `mon, wed, fri at 18:30` or `30 minutes`.
- `sdp_required`: A voice call needs the browser's WebRTC offer. What to do: Send the offer as `sdp`.
- `secret_in_url`: An MCP server's URL carries a credential. What to do: Move it into a header as `${secret:NAME}`.
- `secret_missing`: The file names company secrets that are not stored for this mode: `names` lists them. What to do: Store each with `PUT /v1/secrets/{name}` and a key of this mode, then apply the file again.
- `skill_md_missing`: A skill folder has no SKILL.md at its top. What to do: Add SKILL.md at the top of the folder.
- `skill_name_changed`: A new version of a skill changes the name in its SKILL.md. What to do: Keep the name, or upload a new skill to rename it.
- `skill_name_required`: A skill's SKILL.md has no name. What to do: Give the frontmatter a `name` that starts with a letter.
- `title_required`: A raw text upload to a notebook needs a title. What to do: Send `?title=`.
- `too_many_files`: A skill holds more files than it may. What to do: Keep the files Bash reads, and leave out the rest.
- `too_many_headers`: An MCP server sends more than 20 headers. What to do: Send at most 20.
- `tool_listed_twice`: A tool is in more than one of an MCP server's lists. What to do: Put each tool in one list: `deny`, `require_approval` or `run_without_asking`.
- `tool_name_reserved`: A tool, or an MCP server's slug, takes a name Bash already uses for one of its own tools or tool servers. What to do: Name it something else.
- `tool_not_declared`: A test gives results for a tool the file does not declare. What to do: Declare the tool in `tools`, or drop its results from the test's `tool_results`.
- `tool_not_on_server`: An MCP server's `run_without_asking` names a tool the server does not list. What to do: Use each name exactly as the server lists it (`GET /v1/mcp-servers/{server_id}/tools`), or drop it.
- `tool_policy_twice`: Two tool policies name the same MCP server. What to do: Give each MCP server one policy.
- `tool_schema_invalid`: A tool's input schema is not a JSON Schema object. What to do: Give it `type: object` and its `properties`.
- `unknown_model`: That is not a model a deployment can use. What to do: Use `default`, or an id from `GET /v1/models`.
- `unsafe_path`: A file of a skill has a path that is not allowed. What to do: Use plain relative paths, with no `..` and no leading slash.
- `unsupported_kind`: This kind of interaction cannot be answered, or withdrawn, through the API. What to do: Decline it, or let the user choose in Vestra.
- `url_not_public`: The URL is not a public https address. What to do: Use an https URL that reaches the public internet.
- `user_must_connect`: The user signs in to this app themselves. What to do: Send the user the connect link. Through the API, this interaction can only be declined.
- `user_required`: This company asks for a user on every request, and none was named. What to do: Name the user, or turn off `require_user` with `PATCH /v1/org`.
- `version_not_allowed`: This call runs the current version, so it takes no `@version`. What to do: Drop the `@version`.
- `voice_unknown`: That is not one of the voices. What to do: Use one of the voices the message lists.
- `window_too_long`: The usage window is longer than the API reads at once. What to do: Ask for a shorter window, and page through it.

## 401: Who is calling is not known

- `invalid_api_key`: That API key is not valid. What to do: Send a key from the console, as `Authorization: Bearer vk_test_...`.
- `invalid_session`: The console session is not valid. What to do: Sign in again.
- `key_expired`: This API key has expired. What to do: Use the key that replaced it.
- `key_revoked` (also 409): This API key was revoked: it calls nothing, and it cannot be rolled. What to do: Make a new key in the console or with `POST /v1/keys`.
- `missing_api_key`: The request has no API key. What to do: Send `Authorization: Bearer vk_test_...` (or `vk_live_...`).
- `missing_session`: The console request has no session. What to do: Send your Vestra session as `Bearer <token>`.
- `org_header_required`: A console session must say which company it acts for. What to do: Send the `Vestra-Org` header with your org id.
- `session_ended`: This session has ended, and its token stopped working. What to do: Ask your server for a new session with `POST /v1/sessions`.

## 402: Money stops the call

- `org_cap_reached`: The company's spending cap is reached: its cap for the day (it resets at midnight UTC) or for the month (it resets on the 1st, UTC). What to do: `GET /v1/limits` says which. Raise it with `PATCH /v1/limits`, or wait for it to reset.
- `spend_cap_reached`: A spending cap is reached, the user's or the company's, for the day or the month, so their computer was not woken, or the voice call did not open. What to do: `GET /v1/users/{user_id}` and `GET /v1/limits` say which. Raise it, or wait for it to reset: a day's cap at midnight UTC, a month's on the 1st.
- `storage_quota_exceeded`: This user has used all of their file storage. What to do: Delete files with `DELETE /v1/files/{file_id}` to make room.
- `user_cap_reached`: This user's spending cap is reached: their cap for the day (it resets at midnight UTC) or for the month (it resets on the 1st, UTC). What to do: `GET /v1/users/{user_id}` says what they may still spend. Raise their cap with `PATCH /v1/users/{user_id}`, or wait for it to reset.
- `wallet_empty`: The wallet for this mode is empty. What to do: Add credits in the console, then retry.

## 403: Known, but not allowed

- `account_blocked`: This account or user is blocked. What to do: Contact Vestra support.
- `app_not_allowed`: This app is not on the deployment's list of apps. What to do: Add it to the deployment's `apps`, or pick an app that is there.
- `client_token_not_allowed`: A session's token cannot call this route. What to do: Call it from your server, with an API key.
- `domain_not_allowed`: The page is on a site the user's deployment does not list in `browser.allowed_domains`, or a browser fetch's page went to one. What to do: Add the site to the deployment's `browser.allowed_domains`, or open a page on a site that is there.
- `email_required`: The signed-in Vestra account has no email address, and a company account is made for an email. What to do: Sign in with an account that has an email address, or contact Vestra support.
- `insufficient_role`: This caller cannot do this: a console member without the role, or a session token. What to do: Ask an owner or admin, or call it with an API key.
- `live_mode_required`: Only a live key changes what live users run. What to do: Use a live key, or the console in live mode.
- `not_an_org_member`: You are not a member of this company account. What to do: Ask an owner to invite you, or choose a company you belong to.
- `not_interview_computer`: Only the deployment's own interview can save its draft. What to do: Let the interview finish; it saves the draft itself.
- `org_disabled`: This company account is switched off. What to do: Contact Vestra support.
- `origin_not_allowed`: This page is not an allowed origin for this company. What to do: Add the origin to `allowed_embed_origins` with `PATCH /v1/org`.
- `page_blocked`: The page is on the deployment's `browser.blocked_paths`, such as a payment page, so the browser does not open it or act on it. What to do: Leave payments to the person, on their own device. Change `browser.blocked_paths` on the deployment only if this page should be open to the browser.
- `sandbox_token_required`: Only the interview computer can post this result. What to do: Nothing to do: this route is for Bash itself.
- `scope_escalation`: A key cannot give a scope it does not have. What to do: Make the new key with a key that has the scopes, or ask for fewer.
- `scope_missing`: This key's scopes do not reach this route. What to do: Make a key with the scope the message names, in the console or with `POST /v1/keys`.
- `staff_only`: This route is for Vestra staff. What to do: Nothing to do: it is not part of the API you call.
- `test_mode_required`: Only a test key does this: interviews, and changes to what test users run. What to do: Use a test key, or the console in test mode.
- `user_disabled`: This user is switched off. What to do: Turn them back on with `PATCH /v1/users/{user_id}`.
- `view_only`: This session can watch the screen but not drive it. What to do: Open the session with `screen: "control"` to take the wheel.

## 404: Not found

- `batch_not_found`: No batch by that id, for this key's company and mode. What to do: Check the id, and that the key is of the mode the thing was made in: a test key never sees live data.
- `connection_not_found`: No connected app by that id, for this key's company and mode. What to do: Check the id, and the user it belongs to: list them with `GET /v1/connections`.
- `conversation_not_found`: No conversation by that id, for this key's company and mode. What to do: Check the id and the key's mode. A session token reaches only its own conversation.
- `delivery_not_found`: No webhook delivery by that id, for this key's company and mode. What to do: List the deliveries with `GET /v1/webhooks/{endpoint_id}/deliveries`.
- `deployment_not_found` (also 400): No deployment by that id, for this key's company and mode. What to do: Check the name or `dep_` id. Inside a file or a batch, it answers 400 on the field that names it.
- `deployment_version_not_found`: No deployment version by that id, for this key's company and mode. What to do: List the versions with `GET /v1/deployments/{deployment_id}/versions`.
- `document_not_found`: No notebook document by that id, for this key's company and mode. What to do: List the documents with `GET /v1/notebooks/{notebook_id}/documents`.
- `event_not_found`: No event by that id, for this key's company and mode. What to do: List the recent ones with `GET /v1/events`.
- `file_not_found` (also 400): No file by that id, for this key's company and mode. What to do: Check the id. A file attached to a message answers 400 on that field.
- `interaction_not_found`: No interaction by that id, for this key's company and mode. What to do: List what Bash waits on with `GET /v1/interactions`. A session token reaches only its own.
- `key_not_found`: No API key by that id, for this key's company and mode. What to do: List the keys with `GET /v1/keys`.
- `mcp_server_not_found` (also 400): No MCP server by that id, for this key's company and mode. What to do: List them with `GET /v1/mcp-servers`. Inside a version, it answers 400 on the field.
- `memory_not_found`: No memory by that id, for this key's company and mode. What to do: List what Bash remembers with `GET /v1/memory`.
- `notebook_not_found` (also 400): No notebook by that id, for this key's company and mode. What to do: List them with `GET /v1/notebooks`. Inside a version, it answers 400 on the field.
- `org_not_found`: The company account behind this call was not found. What to do: Contact Vestra support.
- `route_not_found`: No route has that method and path. What to do: Check the path against the API reference, and that it starts `/v1/`.
- `routine_not_found`: No routine by that id, for this key's company and mode. What to do: List them with `GET /v1/routines`.
- `scenario_not_found`: No test scenario by that id, for this key's company and mode. What to do: List them with `GET /v1/deployments/{deployment_id}/scenarios`.
- `secret_not_found`: No secret by that name, for this mode and user. What to do: List the names with `GET /v1/secrets`.
- `session_not_found`: No session by that id, for this key's company and mode. What to do: Check the id. A session ends when its token does.
- `skill_not_found` (also 400): No skill by that id, for this key's company and mode. What to do: List them with `GET /v1/skills`. Inside a file or a version, it answers 400 on the field.
- `task_not_found`: No task by that id, for this key's company and mode. What to do: List the work with `GET /v1/tasks`.
- `test_run_not_found`: No test run by that id, for this key's company and mode. What to do: List them with `GET /v1/deployments/{deployment_id}/test-runs`.
- `tool_call_not_found`: No call to your tool by that id, for this key's company and mode. What to do: Answer the id the interaction gave you.
- `turn_not_found`: No turn by that id, for this key's company and mode. What to do: Check the conversation and the turn id.
- `user_not_found` (also 400): No user by that id, for this key's company and mode. What to do: Users are made the first time you act for them, or with `POST /v1/users`. Reads never make one.
- `webhook_endpoint_not_found`: No webhook endpoint by that id, for this key's company and mode. What to do: List them with `GET /v1/webhooks`.

## 405: Not a method this route takes

- `method_not_allowed`: The MCP endpoint takes POST only. What to do: Send JSON-RPC to `POST /v1/mcp`.

## 409: The state does not allow it now

- `already_a_member`: You already belong to a company account, or this email already made one: a person makes only one of their own. What to do: Open the developer console to use it. To join another, or if yours is gone, ask Vestra to add you.
- `batch_not_running`: That batch has already finished. What to do: Read its items with `GET /v1/batches/{batch_id}/items`.
- `browser_busy`: The user's browser is already running four browser fetches. What to do: Retry after `retry_after_ms`, about a second, or run them one after another.
- `browser_in_use`: A person is using this browser right now, so a browser fetch yields to them. What to do: Retry in a few seconds, when they are done.
- `browser_not_open`: Bash's browser is not open right now. What to do: Ask Bash for work that opens its browser, then try again.
- `call_not_open`: The call ended before its line opened. What to do: Start the call again.
- `conversation_busy`: Bash is still replying in this conversation. What to do: Wait for the reply, or stop it with `POST /v1/conversations/{conversation_id}/stop`.
- `delivery_in_flight`: This delivery is being sent right now. What to do: Try again in a minute.
- `deployment_archived`: This deployment is archived. What to do: Unarchive it with `PATCH /v1/deployments/{deployment_id}` first.
- `deployment_busy`: Several versions were saved at once. What to do: Try again.
- `deployment_changed`: The deployment changed while this ran. What to do: Read the deployment again, then retry.
- `deployment_is_default`: This deployment is a default, for the company or for users, so it cannot be archived. What to do: Set another default with `PATCH /v1/org` or `PATCH /v1/users/{user_id}`, then archive it.
- `deployment_not_live`: This deployment has no live version yet. What to do: Promote a version first, or call it with a test key.
- `idempotency_in_progress`: A request with this Idempotency-Key is still running. What to do: Retry in a moment: you get its answer.
- `idempotency_key_reused`: This Idempotency-Key was used for a different request. What to do: Use a new key for a new request.
- `interaction_already_settled`: This interaction was already answered or withdrawn. What to do: Nothing to do: read it to see how it settled.
- `limits_changed`: The limits changed while you saved. What to do: Read them with `GET /v1/limits` and retry.
- `mcp_server_exists`: An MCP server with this slug already exists. What to do: Use the one there, or give a new slug.
- `mcp_server_live`: A live deployment uses this MCP server. What to do: Change it with a live key, or as an owner or admin.
- `mcp_server_shared`: Another deployment runs this MCP server, and the file would change its settings: `deployments` names them. What to do: Use the same headers in every file that names the server, or give this deployment its own server's URL.
- `memory_not_kept`: This company keeps memory on its own side, so Bash holds none here. What to do: Read the memory from your own store.
- `memory_off`: Memory is off for this company. What to do: Turn it on with `PATCH /v1/org` and a `memory_mode`.
- `memory_panel_off`: Showing a user what Bash remembers is off for this company. What to do: Turn it on with `PATCH /v1/org` and `memory_panel: true`.
- `no_open_tab`: A browser fetch with no url runs on the tab an earlier fetch kept open under its `tab` name, and none is kept under it now. What to do: Send url, with keep_open_ms to keep its tab for the next fetch.
- `no_scenarios`: This deployment has no tests yet, so there is nothing to run. What to do: Add `tests` to vestra.yaml (or draft some with `POST /v1/deployments/{deployment_id}/scenarios/generate`), deploy, then run them.
- `not_a_company_user`: This user cannot be erased through the API. What to do: Contact Vestra support.
- `notebook_busy`: The notebook changed while this call ran. What to do: Retry it.
- `notebook_exists`: A notebook with this slug already exists. What to do: Use the one there, or give a new slug.
- `nothing_to_roll_back`: There is no earlier live version to go back to. What to do: Promote the version you want instead.
- `org_routine_limit_reached`: The company has 200 active routines in this mode. What to do: Delete some; a copy for each user counts too.
- `payment_in_progress`: The person is paying for something in their browser right now; screenshots and the screen are paused until the payment ends. What to do: Try again in a minute.
- `routine_completed`: This routine has finished and does not run again. What to do: Make a new routine.
- `routine_limit_reached`: This user, or your company's own Bash, has as many active routines as it may. What to do: Delete or pause one first.
- `routine_name_taken`: A routine by this name already exists, and the file does not own it. What to do: Rename one of them.
- `routine_not_linked`: This routine was not made through the API, so it has no conversation to run in. What to do: Make the routine with `POST /v1/routines`.
- `routine_paused`: This routine is paused. What to do: Resume it with `PATCH /v1/routines/{routine_id}` and `enabled: true`, then run it.
- `screen_control_unavailable`: Taking control of the screen is not switched on here yet. What to do: Open the session with `screen: "view"`.
- `session_has_no_screen`: This session was opened without a screen. What to do: Open one with `screen: "view"` or `"control"`.
- `session_has_no_voice`: This session was opened without voice. What to do: Open one with `voice: true`.
- `shared_change`: Skills, knowledge and company MCP servers are not versioned yet: every version reads them as they are, so while a deployment runs a version live, a test key or a console developer cannot change them. `deployments` names the live ones. What to do: Change them with a live key (`vestra deploy --live`), which changes them for your users at once, or as an owner or admin.
- `skill_exists`: A skill with this slug already exists. What to do: Upload a new version with `PUT /v1/skills/{skill_id}`, or give a new slug.
- `slug_taken`: A deployment with this slug already exists. What to do: Choose another name, or send a `slug`.
- `test_run_failed`: This version failed its latest test run, so it is not promoted. What to do: Fix it, run the tests again, then promote.
- `test_run_in_progress`: A test run for this version is already running. What to do: Wait for its verdict with `GET /v1/test-runs/{test_run_id}`.
- `test_run_required`: This version has no passing test run. What to do: Run the tests with `POST /v1/deployments/{deployment_id}/test-runs`, then promote.
- `test_runs_busy`: Too many test runs are running: this company has as many as it may, or we do. What to do: Try again when one finishes.
- `too_many_batches`: The company already runs as many batches as it may in this mode. What to do: Wait for one to finish, or cancel one.
- `too_many_scenarios`: The deployment would hold more test scenarios than it may. What to do: Remove scenarios made by hand, or declare fewer tests.
- `tool_call_not_pending`: That call to your tool is not waiting for a result. What to do: Nothing to do: it was answered, or it expired.
- `user_create_in_progress`: Another request is making this user right now. What to do: Retry in a moment.
- `version_never_live`: That version was never live, so there is nothing to roll back to. What to do: Promote it instead.
- `version_not_live`: A live key always runs the live version, so it cannot pin one. What to do: Drop the `@version`, or pin it with a test key.

## 410: Gone

- `stream_expired`: The events after that point are no longer kept. What to do: Read the conversation again with `GET /v1/conversations/{conversation_id}`, and follow its events from now.

## 413: Too large

- `export_too_large`: This user holds more than an export carries. What to do: Ask Vestra support for the export.

## 415: Not a type this route reads

- `unsupported_media_type`: The body is in a type this route does not read. What to do: Send JSON with `Content-Type: application/json`, or the type the route names.

## 429: Slow down

- `export_in_progress`: Another export of this user is being built. What to do: Retry in a minute.
- `rate_limited`: Too many requests. What to do: Slow down, and retry after the `Retry-After` seconds.
- `routine_fire_budget`: This routine, or this company, has used its runs for this hour. What to do: Try again later.
- `too_many_in_flight`: Too many requests at once. What to do: Wait for one to finish, then retry.
- `too_many_streams`: Too many open streams. What to do: Close one, then retry.
- `voice_busy`: Too many voice calls are open. What to do: Retry in a minute.
- `warm_limit`: Many computers are already waking for this company. What to do: Retry in a moment.

## 5xx: Our side: retry

- `auth_unavailable` (503): We could not check your credentials just now. What to do: Retry in a few seconds.
- `box_unavailable` (503): The user's computer did not wake or did not answer, or its browser is not there to use or is paused while a person pays. What to do: Retry in a few seconds, or after the payment finishes when the browser is paused for one.
- `cancel_failed` (500): The interaction was not withdrawn. What to do: Retry the call.
- `connect_failed` (502): The connect link could not be made. What to do: Retry in a moment.
- `connections_unavailable` (502): Connected apps could not be read. What to do: Retry in a moment.
- `database_unavailable` (503): We could not reach our database. What to do: Retry in a few seconds.
- `delete_failed` (502): The file could not be deleted. What to do: Try again.
- `deployment_unavailable` (502): The deployment could not be read. What to do: Retry shortly.
- `disconnect_failed` (502): The app could not be disconnected. What to do: Retry in a moment.
- `download_unavailable` (502): A download link could not be made. What to do: Try again.
- `generator_busy` (503): Drafting tests is slow right now. What to do: Retry after the `Retry-After` seconds.
- `internal_error` (500): Something went wrong on our side. What to do: Retry. If it keeps happening, send Vestra the `request_id`.
- `interview_failed` (502): The interview stopped before its draft was saved. What to do: Start again with a description.
- `mcp_auth_required` (502): The MCP server asked for credentials it was not given. What to do: Give the server's credentials as a header with `${secret:NAME}`.
- `mcp_failed` (500): The MCP request failed on our side. What to do: Retry it.
- `mcp_missing_secret` (502): The MCP server names secrets that are not stored. What to do: Store them with `PUT /v1/secrets/{name}`.
- `mcp_unreachable` (502): The MCP server did not answer. What to do: Check that it is up and reachable from the internet, then check it again.
- `mcp_url_not_public` (502): The MCP server's address is not public. What to do: Use an https URL that reaches the public internet.
- `memory_unavailable` (502): Memory could not be read or deleted. What to do: Retry shortly.
- `memory_write_failed` (502): The memory could not be saved. What to do: Retry shortly.
- `navigation_failed` (502): The page of a browser fetch could not be opened: the site did not answer, or the address was wrong. What to do: Check the address, then retry.
- `not_implemented` (501): This is not available yet. What to do: Nothing to do now: the changelog says when it ships.
- `queue_unavailable` (502): Routines are not taking runs right now. What to do: Try again in a minute.
- `resolve_failed` (500): The answer was not recorded. What to do: Retry the call.
- `return_unavailable` (502): Connect links are not available on this server. What to do: Contact Vestra support.
- `route_failed` (502): A call an MCP tool made inside the API failed. What to do: Retry it.
- `scenario_generation_failed` (502): Tests could not be drafted this time. What to do: Try again, or write the tests yourself in vestra.yaml.
- `screenshot_failed` (502): The browser did not answer with a picture. What to do: Try again.
- `timeout` (504): A browser fetch's page did not send every capture in time. `partial` holds what it did send. What to do: Use what `partial` holds, or retry with a longer `timeout_ms`, up to 20000.
- `tool_result_undelivered` (502): The result could not reach the work. What to do: Try again.
- `upload_failed` (502): The file could not be stored. What to do: Try again.
- `user_create_failed` (502): The user could not be made. What to do: Retry the request.
- `voice_connect_failed` (502): The call could not be opened. What to do: Start it again.
- `voice_unavailable` (503): Voice is not available right now. What to do: Try again later.
