# Error codes

When Maitento refuses a request, it says why with a stable code such as `api:billing:out-of-credit`. This page
lists every one, in 24 areas: 227 codes, and 4 more that are built from a value, such
as `api:<kind>:delete:in-use`. It is generated from Maitento's code, so it lists what the API sends.

Branch on the HTTP status and the code, never on the message: messages are English for developers and logs, and
their wording can change.

## How errors arrive

| Status | Body | Means |
|---|---|---|
| 400 | `{"validationFailures": [{"errorCode", "memberName", "message"}]}` | the request was refused and nothing happened; there is one failure for each reason |
| 403 | `{"code", "error"}`, or empty | you're signed in but not allowed |
| 413 | `{"code": "api:request-too-large", "error", "maxRequestBodyBytes"}` | the body is too large |
| 429 | `{"code": "api:rate-limited", "error", "retryAfterSeconds"}` | slow down; the wait is also sent as `Retry-After` |

In a 400, `errorCode` is the code, `memberName` the request field it is about (`request` when it's about the
whole request), and `message` says what is wrong:

```json
{
  "validationFailures": [
    { "errorCode": "api:credentials:name-in-use", "memberName": "name", "message": "The tenant already has a credential by that name." }
  ]
}
```

A few codes don't arrive as an error: a credential test's result, an embedding test's result, a run's failure
details and the provider sign-in's redirect carry them. Their sections say where. 401 and 404 have no code:
see [Errors](https://docs.maitento.com/reference.md#errors) in the API overview.

### In the SDKs

Every SDK raises a typed error for a refused request:

| Status | TypeScript | Python | C# |
|---|---|---|---|
| 400 | `ValidationError`: `codes`, and `failures`, each with `code`, `member` and `message` | `ValidationError`: `codes`, and `failures`, each with `code`, `member` and `message` | `MaitentoValidationException`: `ValidationResults`, each with `ErrorCode`, `MemberName` and `Message` |
| 403 | `PermissionError`: `code` | `PermissionError`: `code` | `MaitentoApiAuthenticationException`: `StatusCode`, and the body in `ResponseBody` |
| 413 | `RequestTooLargeError`: `code`, `maxRequestBodyBytes` | `RequestTooLargeError`: `code`, `max_request_body_bytes` | `MaitentoApiException`: `StatusCode`, and the body in `ResponseBody` |
| 429 | `RateLimitError`: `code`, `retryAfter` | `RateLimitError`: `code`, `retry_after` | `MaitentoApiException`: `StatusCode`, and the body in `ResponseBody` |

In TypeScript and Python every API error is an `ApiError`, with the `status` and the `code` (a 400's first
failure's), and every SDK error is a `MaitentoError`. In C# every SDK error is a `MaitentoSdkException`.

### Codes about one field

A request body that fails a check on a field answers `api:validation-failure:<field>`, described
[below](#any-request). Some checks answer the plain `api:validation-failure` with the field in `memberName`,
and some answer with the field's path itself as the code, without `api:` (`name`, `request:mount-name`). Treat a
code that doesn't start with `api:` as "this field is wrong", and read `memberName` and the message.

## Codes by area

The Status column gives the HTTP status, and the request field (`memberName`) where a code always names the same
one. Codes that only Maitento's own operators, or its own services, can receive aren't listed.

- [Any request](#any-request): 6 codes
- [Plans and billing](#plans-and-billing): 23 codes
- [Starting runs](#starting-runs): 16 codes
- [Talking to a run](#talking-to-a-run): 4 codes
- [Why a run failed](#why-a-run-failed): 4 codes
- [Listing and keeping runs](#listing-and-keeping-runs): 11 codes
- [Credentials](#credentials): 23 codes
- [Credential tests](#credential-tests): 9 codes
- [Credential stacks](#credential-stacks): 10 codes
- [Model keys](#model-keys): 11 codes
- [Knowledge bases](#knowledge-bases): 22 codes
- [Embedding tests](#embedding-tests): 9 codes
- [Files](#files): 1 code
- [Definitions and namespaces](#definitions-and-namespaces): 7 codes
- [Tools and connectors](#tools-and-connectors): 2 codes
- [Automations](#automations): 2 codes
- [Webhooks](#webhooks): 4 codes
- [Build history](#build-history): 3 codes
- [Usage](#usage): 7 codes
- [Users, API keys and invitations](#users-api-keys-and-invitations): 15 codes
- [Signing up and signing in](#signing-up-and-signing-in): 14 codes
- [Signing in with a provider](#signing-in-with-a-provider): 25 codes
- [Notifications](#notifications): 1 code
- [Cloud shell](#cloud-shell): 2 codes

## Any request

Codes any call can answer with.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:forbidden` | 403 | You're signed in, but not allowed to do this, and no more specific reason applies. |  |
| `api:rate-limited` | 429 | Too many requests in a short time. | Wait `retryAfterSeconds` (also sent as `Retry-After`), then try again. The SDKs wait and retry for you where that is safe. |
| `api:request-too-large` | 413 | The request body is over this call's limit, which the answer's `maxRequestBodyBytes` gives. | Send a smaller body. |
| `api:validation-failure` | 400 | The request failed a check. `memberName` names the field (`request` when the body is missing), and the message says what is wrong. | Check what you entered and try again. |
| `api:validation-failure:<field>` | 400 | The request body failed a check on a field. `<field>` is the field's path in kebab case, with `:` between levels: `api:validation-failure:system-prompt`, `api:validation-failure:credentials[0]:value`. `memberName` is the same field, and the message says what is wrong. |  |
| `api:validation-failure:interaction` | 400 | The interaction definition failed validation. There is one failure for each problem, and its message says what to fix. |  |

## Plans and billing

What your plan lets you start and keep, and buying or changing a plan. A start your plan refuses creates nothing. [Limits](https://docs.maitento.com/concepts/limits.md#your-plan) explains the refusals, and [Plans and billing](https://docs.maitento.com/concepts/plans-and-billing.md) the plans.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:billing:alert-threshold-invalid` | 400 · `billing` | A low-balance alert percentage outside 1 to 100. | Choose a share of the last top-up from 1 to 100 percent, or turn the email off. |
| `api:billing:allowance-used` | 400 · `billing` | The plan's allowance for this kind of work is used up for the period, and the plan can't top up. | Upgrade to carry on now. |
| `api:billing:already-subscribed` | 400 · `billing` | Your tenant already has a paid plan. | Change that plan instead of buying another. |
| `api:billing:auto-top-up-threshold-invalid` | 400 · `billing` | An auto top-up threshold that's negative, past the largest top-up, or not in whole cents. | Choose a balance from 0 to the largest top-up, in whole cents, to start an automatic top-up. |
| `api:billing:change-awaiting-payment` | 400 · `billing` | A change while an earlier one still waits for its invoice to be paid. | Pay it, or wait for it to lapse, then try again. |
| `api:billing:invalid-return-path` | 400 · `billing` | The `returnPath` isn't a plain path in the portal, such as `/billing`, for the payment page to send you back to. |  |
| `api:billing:lanes-over-limit` | 400 · `billing` | That's more extra lanes than this plan can hold. | Choose fewer. |
| `api:billing:mixed-change` | 400 · `billing` | A change that adds something and takes something away at once; made as two changes. | Make the addition now, and the reduction (which waits for your renewal) as a separate change. |
| `api:billing:no-billing-account` | 400 · `billing` | There's no billing account yet, because nothing has been bought. | Buy a plan or a top-up first. |
| `api:billing:no-change` | 400 · `billing` | The plan asked for is the plan the tenant already has. |  |
| `api:billing:no-subscription` | 400 · `billing` | A plan change, cancellation or withdrawal on a tenant with no paid subscription. | Choose a plan to buy one. |
| `api:billing:nothing-scheduled` | 400 · `billing` | Nothing is booked for the renewal, so there's nothing to withdraw. |  |
| `api:billing:out-of-credit` | 400 · `billing` | The tenant is out of credit: top up or upgrade. | Top up or upgrade, then try again. |
| `api:billing:payments-not-configured` | 400 · `billing` | Payments aren't available on this Maitento, so nothing can be bought here. |  |
| `api:billing:premium-not-included` | 400 · `billing` | The plan doesn't include capsules and code pipelines. | Upgrade to run them. |
| `api:billing:storage-cap-reached` | 400 · `billing` | The write would take the tenant over its storage cap, and its plan can't pay for storage above it. | Delete what you no longer need, or upgrade. |
| `api:billing:subscription-frozen` | 400 · `billing` | A plan change while frozen after a failed renewal: the failed invoice is paid first. | Pay it from the payment provider's page before changing the plan. |
| `api:billing:tenant-locked` | 400 · `billing` | A platform admin has locked the tenant: nothing new starts until it's unlocked. | Contact support to have it unlocked. |
| `api:billing:tier-not-available` | 400 · `billing` | That plan can't be bought. | Choose another. |
| `api:billing:top-up-amount-invalid` | 400 · `billing` | The top-up amount is less than the smallest top-up, more than the largest, or not in whole cents. | Choose an amount between the smallest and the largest top-up, in whole cents. |
| `api:billing:top-up-not-available` | 400 · `billing` | Your plan can't buy credit. | Upgrade to a paid plan to top up. |
| `api:file-system:limit-reached` | 400 · `fileSystems` | The plan holds no more file systems. | Delete one you no longer use, or upgrade. |
| `api:partition:limit-reached` | 400 · `partitions` | The plan holds no more knowledge bases. | Delete one you no longer use, or upgrade. |

## Starting runs

Starting an interaction or an app. A start that is refused creates nothing. Child runs are refused the same way, and the refusal is what their start returns.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:apps:input-missing` | 400 · `inputs` | An app run was started without a value for an input the app declares. |  |
| `api:interactions:ai-model-override-invalid` | 400 · `aiModelOverrides` | An AI model override is malformed, names an unknown model, or one that cannot run an interaction. |  |
| `api:interactions:input-empty` | 400 · `inputs` | A required input was given empty, and it has no default. |  |
| `api:interactions:input-invalid` | 400 · `inputs` | An input's value is not of its type (a number, a date, a secret's id…). |  |
| `api:interactions:input-required` | 400 · `inputs` | A required input was not given (member `inputs`; the message names it). |  |
| `api:interactions:input-unknown` | 400 · `inputs` | An input the interaction does not declare was given. |  |
| `api:interactions:loom-gift-invalid` | 400 · `loomGifts` | Threads handed to an agent at start are refused (too many, unknown, or private to their loom). |  |
| `api:interactions:loom-mapping-invalid` | 400 · `loomMappings` | A loom mapping is malformed: an unknown agent, no id or name, or a namespace given twice. |  |
| `api:interactions:loom-not-enabled` | 400 | Memory (looms) was mapped or handed over for an interaction that has none. |  |
| `api:interactions:loom-not-found` | 400 · `loomMappings` | A loom, or the namespace named to find it in, does not exist. |  |
| `api:interactions:no-enabled-version` | 400 · `interactionId` | Every version of the interaction is disabled. |  |
| `api:interactions:not-found` | 400 · `interactionId` | The interaction does not exist in the tenant. |  |
| `api:interactions:response-schema-invalid` | 400 · `responseJsonSchema` | The `responseJsonSchema` given with the start is not a valid JSON schema. |  |
| `api:interactions:swarm-limits-invalid` | 400 · `swarmLimits` | The start's sub-interaction limits are out of range. |  |
| `api:web:download-partition-invalid` | 400 | The knowledge base named for the run's web downloads can't be used: it isn't one of your tenant's temporary knowledge bases. | Name one of your tenant's temporary knowledge bases. |
| `api:web:download-partition-required` | 400 | The run's agents may fetch from the web, and nothing names a knowledge base to keep what they fetch: not the start, the namespace or the tenant. | Name a temporary knowledge base for the run's downloads, or set a default one for the namespace or the tenant. |

## Talking to a run

Sending a message or a vote to an interaction run.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:interaction-process:complete` | 400 · `Request` | The run has ended (Finished, Error, Killed); nothing was stored. Permanent. |  |
| `api:interaction-process:no-vote-pending` | 400 · `Request` | A vote was sent while no vote or solution proposal is open. |  |
| `api:interaction-process:not-awaiting-input` | 400 · `Request` | A vote needs the run parked at a human-input gate, and it is not (it is running). Retry later. | Try again when it asks. |
| `api:interaction-process:vote-pending` | 400 · `Request` | The run is parked on a vote or solution proposal: it takes a vote, not a message. | Vote instead of sending a message. |

## Why a run failed

A run that can't make a model call ends with status `Error`, and its failure details end with one of these codes in brackets: `... [api:credentials:all-failed]`. See [when the stack can't serve a call](https://docs.maitento.com/concepts/credentials.md#when-the-stack-cant-serve-a-call) and [when there's no key](https://docs.maitento.com/concepts/models-and-keys.md#when-theres-no-key).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:credentials:all-failed` | In the run | Every credential that could serve the call failed. | Replace the key, then retry. Test the credential to see which key fails. |
| `api:credentials:no-usable-credential` | In the run | No credential in the run's stack serves this model, or every one that does is switched off. | Add a credential that serves the model to the stack, or change the entries' rules. |
| `api:model-keys:all-failed` | In the run | Every key a model call could use for the provider failed (authentication, quota or endpoint), and the platform's key is not allowed. Carried at the end of a run's failure text, in brackets. | Check the key with the provider, and replace it. |
| `api:model-keys:none-for-provider` | In the run | A model call had no key to run on: none supplied with the run, none of the tenant's for the provider, and the platform's key not allowed. Carried at the end of a run's failure text, in brackets. | Add a model key for the provider, or give the run a credential stack. |

## Listing and keeping runs

Listing runs, and how long they are kept.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:processes:invalid-cursor` | 400 · `cursor` | The `cursor` isn't one this list gave out. | Start again from the first page. |
| `api:processes:invalid-ids` | 400 · `ids` | An id isn't a run id, or more than 50 are asked for at once. |  |
| `api:processes:invalid-namespaces` | 400 · `namespace` | More than 50 namespaces are asked for at once. |  |
| `api:processes:invalid-state` | 400 · `state` | The `state` isn't `active`, `waiting` or `terminal`. |  |
| `api:processes:invalid-status` | 400 · `statuses` | A status isn't one that the run types asked for can have. |  |
| `api:processes:invalid-type` | 400 · `types` | A type isn't `App`, `Interaction`, `Capsule` or `CodePipeline`. |  |
| `api:processes:retention:lease-inherited` | 400 · `processId` | The run's lease is inherited from a retained ancestor — release that run instead. | Release that run instead. |
| `api:processes:retention:lease-limit-reached` | 400 · `processId` | The tenant already holds its maximum of own leases. | Release runs you no longer need, then try again. |
| `api:processes:retention:note-too-long` | 400 · `note` | The note is too long. | Keep it to 500 characters. |
| `api:processes:summary:invalid-hours` | 400 · `hours` | `hours` isn't from 1 to 168. |  |
| `api:tenant:retention-out-of-range` | 400 · `hours` | The retention asked for is outside what your plan allows. | Choose a retention your plan allows, in whole hours. Upgrade to keep runs longer. |

## Credentials

Adding and changing [credentials](https://docs.maitento.com/concepts/credentials.md), and subscription sign-ins for code pipelines.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:credentials:cloud-invalid` | 400 | Check the cloud settings: Bedrock and Vertex are for Anthropic credentials, with a region (and for Vertex a project) and no endpoint. |  |
| `api:credentials:endpoint-invalid` | 400 · `endpoint` | An endpoint is a whole https:// address, with no user name, password, query or fragment. |  |
| `api:credentials:endpoint-not-public` | 400 · `endpoint` | That endpoint isn't on the public internet. Maitento only calls public https endpoints. |  |
| `api:credentials:key-invalid` | 400 · `keys` | A key is one piece of text with no spaces, at most 4096 characters (a Vertex key is the whole JSON key file). |  |
| `api:credentials:key-unknown` | 400 · `keys` | A key on this credential changed while you were editing it. Close this and open it again. |  |
| `api:credentials:keys-required` | 400 · `keys` | Add at least one key. |  |
| `api:credentials:limit-reached` | 400 · `credentials` | Your plan holds no more credentials. | Delete one you no longer use, or change plan. |
| `api:credentials:models-invalid` | 400 · `models` | Check the models: each needs a name with no spaces, listed once, with a positive context window if you give one. |  |
| `api:credentials:name-in-use` | 400 · `name` | You already have a credential with that name. | Choose another. |
| `api:credentials:name-required` | 400 | Give the credential a name. |  |
| `api:credentials:name-too-long` | 400 · `name` | A credential's name is at most 100 characters, with no line breaks. |  |
| `api:credentials:not-a-sign-in` | 400 | That credential isn't a sign-in for this coding agent. |  |
| `api:credentials:provider-invalid` | 400 · `provider` | Choose a provider. |  |
| `api:credentials:sign-in-code-required` | 400 · `code` | Paste the code the provider showed after you signed in. |  |
| `api:credentials:sign-in-fixed` | 400 · `credential` | A sign-in's provider, endpoint and tokens come from signing in. | Sign in again to change them. |
| `api:credentials:sign-in-refreshed-by-caller` | 400 · `credentialId` | The application that brought this sign-in keeps it fresh, so it can't be signed in again here. |  |
| `api:credentials:sign-in-refreshed-here` | 400 · `id` | Maitento keeps this sign-in fresh itself. | Sign in again to replace its tokens. |
| `api:credentials:sign-in-refused` | 400 · `code` | The provider refused that code. Copy it again, whole, from the page it showed you, or start again. |  |
| `api:credentials:sign-in-tokens-invalid` | 400 | The sign-in's tokens are incomplete. | Send the access and refresh tokens, Codex's ID token, and when the access token expires. |
| `api:credentials:sign-in-unavailable` | 400 | The provider's sign-in isn't answering right now. | Try again in a few minutes. |
| `api:credentials:sign-in-unknown` | 400 · `signInId` | That sign-in has finished or expired. | Start again. |
| `api:credentials:tag-invalid` | 400 · `tag` | A tag is at most 100 characters, with no line breaks. |  |
| `api:credentials:too-many-keys` | 400 · `keys` | A credential holds at most 10 keys. |  |

## Credential tests

A [test of a credential](https://docs.maitento.com/concepts/credentials.md#test-a-credential) answers 200 whether it passes or not. Each key that fails has one of these codes as its `errorCode`, and the result's own `errorCode` is the first failing key's.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:credentials:test-auth-failed` | In the result | The provider refused the key. |  |
| `api:credentials:test-endpoint-not-found` | In the result | Nothing answers at the endpoint's models route. | Check the endpoint. |
| `api:credentials:test-endpoint-refused` | In the result | Maitento won't connect to that endpoint's address. |  |
| `api:credentials:test-not-supported` | In the result | Claude on Bedrock or Vertex can't be tested here. |  |
| `api:credentials:test-provider-error` | In the result | The provider had an error of its own. | Try again later. |
| `api:credentials:test-quota-exhausted` | In the result | The key's account is out of quota or can't pay. |  |
| `api:credentials:test-rejected` | In the result | The provider turned the request down. |  |
| `api:credentials:test-timed-out` | In the result | The endpoint didn't answer in time. |  |
| `api:credentials:test-unreachable` | In the result | The endpoint couldn't be reached. |  |

## Credential stacks

Making and using [credential stacks](https://docs.maitento.com/concepts/credentials.md#put-credentials-in-a-stack).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:credential-stacks:credential-unknown` | 400 · `entries` | A credential in this stack no longer exists. | Remove it, then save. |
| `api:credential-stacks:entries-invalid` | 400 · `entries` | Check the entries: a stack has 1 to 20 credentials, and each rule names at least one model or endpoint kind. |  |
| `api:credential-stacks:in-use-as-default` | 400 · `id` | This stack is a default, for the tenant or a namespace, so it can't be deleted. | Change that default first. |
| `api:credential-stacks:in-use-by-partition` | 400 · `id` | A knowledge base embeds with this stack, so it can't be deleted. | Give the knowledge base another stack, or clear its stack, first. |
| `api:credential-stacks:limit-reached` | 400 · `credentialStacks` | Your plan holds no more credential stacks. | Delete one you no longer use, or change plan. |
| `api:credential-stacks:name-in-use` | 400 · `name` | You already have a stack with that name. | Choose another. |
| `api:credential-stacks:name-required` | 400 | Give the stack a name. |  |
| `api:credential-stacks:name-too-long` | 400 · `name` | A stack's name is at most 100 characters, with no line breaks. |  |
| `api:credential-stacks:unknown` | 400 | That stack doesn't exist in this tenant. |  |
| `api:credential-stacks:with-credentials` | 400 | A run can't have a stack and keys of its own at once. | Choose one. |

## Model keys

Adding and changing your tenant's [model keys](https://docs.maitento.com/concepts/models-and-keys.md#add-a-model-key).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:model-keys:api-key-invalid` | 400 · `apiKey` | The key is too long, or holds whitespace or control characters. | Paste the key on its own: no spaces or line breaks, and at most 4,096 characters. |
| `api:model-keys:api-key-required` | 400 · `apiKey` | No key was given on create (or an empty one on update). | Paste the API key. |
| `api:model-keys:base-url-invalid` | 400 · `baseUrl` | Not an absolute https URL with a host and no embedded credentials. | Enter a full https URL, without a user name or password. |
| `api:model-keys:base-url-not-allowed` | 400 · `baseUrl` | The host is not a public endpoint (loopback, private, link-local or an internal name). | Use a public endpoint. |
| `api:model-keys:label-required` | 400 · `label` | No label was given. | Give the key a label. |
| `api:model-keys:label-too-long` | 400 · `label` | The label is longer than 100 characters. | Use at most 100 characters for the label. |
| `api:model-keys:limit-reached` | 400 · `provider` | The tenant already holds the most keys it may. | Delete keys you no longer use, then try again. |
| `api:model-keys:model-name-overrides-invalid` | 400 · `modelNameOverrides` | A blank or over-long name, or too many entries. | Check the model name overrides: each needs both names, up to 200 characters each, and a key can have at most 50. |
| `api:model-keys:priority-out-of-range` | 400 · `priority` | Outside 0–1000. | Choose a priority from 0 to 1000. |
| `api:model-keys:provider-invalid` | 400 · `provider` | The provider is not one a key can be stored for. | Choose a provider a model key can be for. |
| `api:model-keys:provider-required` | 400 · `provider` | No provider was given. | Choose a provider. |

## Knowledge bases

Knowledge bases (partitions in the API) and the embedding models that index them. A file that couldn't be indexed has `api:embeddings:fallback-not-permitted` or `api:embeddings:credentials-exhausted` in its `indexingFailure`. See [Knowledge](https://docs.maitento.com/concepts/knowledge.md).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:embeddings:credential-api-key-required` | 400 · `credentials` | An embedding credential without an API key. |  |
| `api:embeddings:credential-base-url-invalid` | 400 · `baseUrl` | A credential's base URL is not an absolute https URL. |  |
| `api:embeddings:credential-base-url-not-allowed` | 400 · `baseUrl` | An embedding credential's base URL is not a public provider endpoint. |  |
| `api:embeddings:credential-base-url-required` | 400 | A credential for a provider with no default endpoint (Qwen) carries no base URL. |  |
| `api:embeddings:credential-id-duplicate` | 400 · `credentials` | Two embedding credentials share a credential id. |  |
| `api:embeddings:credential-id-required` | 400 · `credentials` | An embedding credential without a credential id. |  |
| `api:embeddings:credential-provider-mismatch` | 400 | A credential is for a different provider from the partition's profile. |  |
| `api:embeddings:credentials-empty-without-consent` | 400 · `credentials` | An empty embedding credential stack without consent to Maitento's key. |  |
| `api:embeddings:credentials-exhausted` | 400 · `partition` | Every key that could embed for the knowledge base failed, or is switched off for failing, and Maitento's own key may not be used. | Check the keys. |
| `api:embeddings:fallback-not-permitted` | 400 · `partition` | Nothing can embed for the knowledge base: it has no embedding credentials or stack of its own, your tenant has no model key for the provider, and Maitento's own key may not be used for it. | Add a model key for the provider, or give the knowledge base credentials or a stack. |
| `api:embeddings:index-rebuild-failed` | 400 · `partition` | Search is refused: rebuilding the knowledge base's index for its new embedding model stopped, because its keys failed. | Fix the keys, and the rebuild carries on. |
| `api:embeddings:index-rebuilding` | 400 · `partition` | Search is refused while the knowledge base's index is rebuilt for a new embedding model, because a half-built index would answer from only some of the files. The message says how far it has got. | Try again when it is done. |
| `api:embeddings:invalid-profile` | 400 · `profile` | The embedding profile isn't valid. The message says why. |  |
| `api:embeddings:profile-changed` | 400 | The knowledge base's embedding model changed while its credentials or stack were being set. | Set them again. |
| `api:embeddings:reindex-in-progress` | 400 · `profile` | The knowledge base's index is already being rebuilt. | Wait for it to finish before changing the embedding model again. |
| `api:embeddings:reindex-not-permitted` | 400 · `profile` | The profile asked for needs the index rebuilt, and the request set `allowReindex` to `false`. Nothing was changed. |  |
| `api:partition:files:invalid-cursor` | 400 · `cursor` | The `cursor` isn't one this list gave out. | Start again from the first page. |
| `api:partition:lifetime-not-temporary` | 400 · `fileLifetimeHours` | Only a temporary partition has a file lifetime. |  |
| `api:partition:lifetime-out-of-range` | 400 · `fileLifetimeHours` | A temporary partition's file lifetime is outside 1–720 hours. |  |
| `api:partition:not-empty` | 400 · `id` | A knowledge base still holds files, so it cannot be deleted. Delete them first. |  |
| `api:partition:temporary-not-indexed` | 400 · `id` | A temporary partition is never indexed, so it has no embedding model to change. |  |
| `api:partition:too-many-ids` | 400 · `ids` | More partition ids than one status request may name. |  |

## Embedding tests

A test call of an embedding model (`POST /partitions/embedding-profile/probe`) says what went wrong in its result's `errorCode`. A change to a custom embedding model makes the same call first, and is refused with 400 and the same code when it fails.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:embeddings:probe-credential-rejected` | In the result, or 400 | Every key was refused (401/403, or Gemini's 400 for an invalid key). |  |
| `api:embeddings:probe-dimension-mismatch` | In the result, or 400 | The model answered, but its embeddings have a different number of dimensions from the profile's. The result says how many it measured. |  |
| `api:embeddings:probe-endpoint-unreachable` | In the result, or 400 | The base URL's host does not exist, or refused a secure connection. |  |
| `api:embeddings:probe-failed` | In the result, or 400 | Anything else: a refused request, a provider outage, a response that was not an embedding. |  |
| `api:embeddings:probe-model-not-found` | In the result, or 400 | The endpoint answered 404: the model id (or the base URL's path) is wrong. |  |
| `api:embeddings:probe-needs-credentials` | 400 | A test call, or a custom embedding model, needs at least one key of your own. It never uses Maitento's. |  |
| `api:embeddings:probe-quota-exceeded` | In the result, or 400 | The account behind the key cannot pay for the call, or is over a quota. |  |
| `api:embeddings:probe-rate-limited` | In the result, or 400 | The provider is rate-limiting the key; try again shortly. |  |
| `api:embeddings:probe-timed-out` | In the result, or 400 | The call did not finish in time. |  |

## Files

Files in a knowledge base.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:files:update-conflict` | 400 · `file` | Something else changed the file while it was being updated. Nothing was changed. | Try again. |

## Definitions and namespaces

Deploying, changing and deleting definitions and namespaces. A delete that is blocked answers 400 with one failure for each reason, and each failure's message names what blocks it.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:<kind>:delete:has-active-runs` | 400 · `id` | It has runs that are still going, so it can't be deleted. The message lists them. `<kind>` is `agent`, `app`, `capsule`, `code-pipeline` or `interaction`. | Wait for them to end, or kill them. |
| `api:<kind>:delete:in-use` | 400 · `id` | The latest version of another definition uses this one, so it can't be deleted. The message lists them. `<kind>` is `agent`, `connector`, `interaction` or `tool`. | Remove it from those definitions, or delete them first. |
| `api:<kind>:delete:referenced-by-automation` | 400 · `id` | An automation starts it, so it can't be deleted. The message names the automations. `<kind>` is `app`, `capsule`, `code-pipeline` or `interaction`. | Remove it from the automation first. |
| `api:definitions:version-conflict` | 400 · `versions` | Another change, such as a second deploy of the same namespace, kept adding versions to the definition while this one was being added. Nothing was changed. | Try again. |
| `api:namespace:delete:in-progress` | 400 · `id` | A delete of a namespace another delete is checking right now. | Wait for that delete to finish. |
| `api:namespace:delete:not-empty` | 400 · `id` | The namespace still holds definitions, automations, memory or file systems. The message lists them. | Delete them first. |
| `api:namespace:deleting` | 400 · `namespaceId` | This namespace is being deleted, so nothing can be created in it. | Choose another namespace. |

## Tools and connectors

Tools, and the connectors they call. See [Tools](https://docs.maitento.com/concepts/tools.md).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:connectors:address-blocked` | 400 · `url` | The connector's address is not one Maitento will call: a loopback, private, link-local or cluster address, a name that resolves to one, an internal name such as `localhost`, or one of the platform's own hosts. Refused when the connector is deployed if the URL names such an address outright, and when a tool is called (including on every redirect) if its name resolves to one. | Use a public address. |
| `api:connectors:secret-missing` | 400 · `externalOperations` | The tool's connector needs a secret in a header (`auth = "Header"`), and nothing gives it one: not the tool's own `auth`, not the interaction's binding, and no run input. Refused when an interaction that can never supply it is deployed; when the secret is an optional run input that a run was not given, calling the tool answers with this code. | Set the tool's auth, its binding's auth, or accept the secret as a run input. |

## Automations

Creating and triggering [automations](https://docs.maitento.com/concepts/automations.md).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:automations:app-input-missing` | 400 · `actions` | On create and update: an action starts an app version without a value for one of the inputs it declares (its `main`'s parameters). Every app input is required. |  |
| `api:automations:start-failed` | 400 · `actions` | From a manual trigger: none of the automation's actions could be started. The message says why; the failures that follow it carry the cause's own codes. Each failure is in the history too. |  |

## Webhooks

Registering webhooks, and delivering to them.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:webhook:header-value-required` | 400 · `headers` | A header has no value, and there is no stored value to keep. | Send its value. |
| `api:webhook:secret-required` | 400 · `secret` | An update that moves the registration to a new origin without supplying the secret again. |  |
| `api:webhooks:address-blocked` | 400 · `url` | The webhook's address is not one Maitento will deliver to: a loopback, private, link-local or cluster address, a name that resolves to one, an internal name such as `localhost`, or one of the platform's own hosts. Refused when the webhook is registered if the URL names such an address outright; otherwise each delivery to it is recorded as failed with this code in its failure details. | Use a public address. |
| `api:webhooks:already-exists` | 400 · `url` | A live registration already has this URL and type. |  |

## Build history

Listing a namespace's builds.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:builds:invalid-cursor` | 400 · `cursor` | The `cursor` isn't one this list gave out. | Start again from the first page. |
| `api:builds:invalid-namespace` | 400 · `namespace` | No namespace was named. |  |
| `api:builds:invalid-outcome` | 400 · `outcome` | The `outcome` filter isn't `Succeeded`, `Failed` or `Rejected`. |  |

## Usage

Reading your usage.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:usage:invalid-bucket` | 400 · `bucket` | Hourly totals cover at most 31 days. | Choose daily or weekly totals for a longer range. |
| `api:usage:invalid-dimension` | 400 · `groupBy` | Usage can't be grouped that way. |  |
| `api:usage:invalid-filter` | 400 | One of the filters isn't valid. | Remove it and try again. |
| `api:usage:invalid-metric` | 400 · `metric` | That measure isn't available. |  |
| `api:usage:invalid-namespaces` | 400 · `namespace` | More than 50 namespaces are asked for at once. | Filter by at most 50 namespaces at a time. |
| `api:usage:invalid-range` | 400 | That date range can't be shown. | Choose a start before the end, at most 400 days apart. |
| `api:usage:invalid-sort` | 400 · `sort` | Usage can't be sorted that way. |  |

## Users, API keys and invitations

The people in your tenant, their API keys, and inviting more.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:invitation:code-no-remaining-uses` | 400 · `invitationCodeId` | You've used every invitation on your code. | Cancel a pending invite to free one up. |
| `api:invitation:email-address-already-in-use` | 400 · `emailAddress` | Someone already has an account with this email address. |  |
| `api:invitation:email-address-already-invited` | 400 · `emailAddress` | You've already invited this email address, and that invite hasn't been used yet. |  |
| `api:invitation:grants-admin-requires-admin` | 403 | Only a tenant admin can invite another admin. |  |
| `api:invitation:grants-admin-requires-invite-to-tenant` | 400 · `grantsAdmin` | An admin invite has to invite the person into this tenant. |  |
| `api:invitation:invalid-scope` | 400 · `scope` | That list of invites isn't available. |  |
| `api:invitation:invite-to-tenant-requires-admin` | 403 | Only a tenant admin can invite people into this tenant. |  |
| `api:invitation:no-matching-codes` | 400 · `invitationCodeId` | That invitation code doesn't exist or isn't yours. |  |
| `api:invitation:tenant-scope-requires-admin` | 403 | Only a tenant admin can see everyone's invites. |  |
| `api:invitation:too-many-pending-invites` | 400 · `emailAddress` | This tenant already has 100 invites waiting to be accepted. | Cancel the ones you no longer need, then try again. |
| `api:invitation:used-or-cancelled` | 400 · `invitationId` | This invite has already been used or cancelled. |  |
| `api:user:api-key-create:expiry-in-past` | 400 · `dateExpires` | The API key's expiry date is in the past. | Choose an expiry date in the future. |
| `api:user:api-key-create:name-exists` | 400 · `name` | You already have an API key with this name. |  |
| `api:user:cannot-modify-own-account` | 400 · `userId` | You can't change your own role or status. |  |
| `api:user:email-already-verified` | 400 · `id` | This email address is already verified. You can sign in. |  |

## Signing up and signing in

Signing up a new tenant (`POST /tenants/register`), joining one (`POST /users/register`), and signing in with a password.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:auth:email-not-verified` | 400 · `EmailAddress` | Your email address hasn't been verified yet. | Open the verification link we emailed you, then sign in. |
| `api:tenant:register:code-not-found` | 400 · `code` | Sign-up without an invitation isn't open. If someone has invited you, open the invitation link in your email. |  |
| `api:tenant:register:email-domain-blocked` | 400 · `emailAddress` | Sign-up isn't open to this email domain. | Use a different address. |
| `api:tenant:register:email-in-use` | 400 · `emailAddress` | This email address has already been used to sign up. | Sign in instead. |
| `api:tenant:register:invite-code-mismatch-required` | 400 · `inviteId` | The invite and the invitation code don't match. |  |
| `api:tenant:register:invite-email-mismatch` | 400 · `emailAddress` | The invite was sent to a different email address. |  |
| `api:tenant:register:invite-not-found` | 400 · `inviteId` | The invite doesn't exist, or has been used or cancelled. |  |
| `api:tenant:register:invite-not-valid-for-new-tenant-registration` | 400 · `inviteId` | The invite is to join an existing tenant, not to sign up a new one. | Join with `POST /users/register` instead. |
| `api:tenant:register:sign-up-closed` | 400 · `emailAddress` | Sign-up isn't open here. | Ask a tenant admin to invite you. |
| `api:tenant:register:verification-failed` | 400 · `turnstileToken` | We couldn't confirm you're not a robot. | Complete the check again, then sign up. |
| `api:user:register:email-in-use` | 400 · `emailAddress` | An account already exists for this email address. | Sign in instead. |
| `api:user:register:invite-email-mismatch` | 400 · `emailAddress` | This invitation was sent to a different email address. |  |
| `api:user:register:invite-not-found` | 400 · `inviteId` | This invitation is no longer valid. |  |
| `api:user:register:invite-not-valid-for-joining-tenant-registration` | 400 · `inviteId` | This invitation can't be used to join a tenant. |  |

## Signing in with a provider

Signing in, or signing up, with an identity provider runs in the browser. When it fails, the browser is sent back to the portal with the code in the `error` query parameter ("Redirect" below). Finishing a sign-in (`POST /auth/sso/exchange`), and linking or unlinking a provider, answer with the code instead.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:auth:sso:account-disabled` | 400 | The account is disabled. | Ask an admin of your team to enable it. |
| `api:auth:sso:account-not-verified` | 400 · `code` | The account's e-mail address hasn't been verified. | Open the verification link we emailed you, then sign in. |
| `api:auth:sso:browser-mismatch` | Redirect | The provider came back to a browser other than the one that started the sign-in. | Start it again in the browser you want to use. |
| `api:auth:sso:cancelled` | Redirect | The person cancelled at the provider (or refused consent). |  |
| `api:auth:sso:email-domain-blocked` | Redirect | Signing up: the address's domain can't be used to sign up. | Use an account with a different address. |
| `api:auth:sso:email-in-use` | Redirect | Signing up: the address, or a variant of it, is in use or has signed up before. | Sign in instead. |
| `api:auth:sso:email-not-verified` | 400 · `code` | Signing up: the provider didn't say the identity's e-mail address is verified. | Verify it with the provider, then try again. |
| `api:auth:sso:exchange-failed` | Redirect | The provider's code couldn't be redeemed, or what it returned couldn't be trusted. | Try again. |
| `api:auth:sso:identity-linked-to-another-account` | 400 · `provider` | The identity is linked to another account. | Unlink it there first, or use another one. |
| `api:auth:sso:interactive-session-required` | 403 | Only a person's own signed-in session can change how they sign in: not an API key or a run's token. |  |
| `api:auth:sso:invalid-code` | 400 · `code` | The one-time code is unknown, expired, used, for another purpose, or doesn't match the verifier. | Start it again. |
| `api:auth:sso:invalid-link-token` | 400 · `token` | The link-confirmation link is unknown, expired, used or wrong. |  |
| `api:auth:sso:invalid-request` | Redirect | A start without a valid `codeChallenge`, or with an unknown `intent`. | Try again. |
| `api:auth:sso:invalid-state` | Redirect | The provider came back with a state that is missing, unknown, expired, already used or another provider's. | Start it again. |
| `api:auth:sso:last-sign-in-method` | 400 · `provider` | The identity is the account's only way to sign in, so it can't be unlinked. | Set a password or link another provider before you remove it. |
| `api:auth:sso:link-confirmation-required` | Redirect | The identity's verified address is an existing account's, which isn't linked to it: nothing is linked automatically, and the account's owner has been e-mailed a link to confirm it. |  |
| `api:auth:sso:no-account` | Redirect | Signing in: no account is linked to the identity, and none has its verified address. | Sign in another way, or use the account you joined with. |
| `api:auth:sso:no-pending-link` | 400 · `pendingLink` | There is no link waiting for confirmation. |  |
| `api:auth:sso:provider-already-linked` | 400 | The account already has an identity at that provider; unlink it first. | Unlink it before linking a different one. |
| `api:auth:sso:provider-error` | Redirect | The provider came back with an error, or without a code. | Try again in a moment. |
| `api:auth:sso:provider-not-enabled` | Redirect | The provider isn't one Maitento knows, or isn't set up on this deployment. | Use another way to sign in. |
| `api:auth:sso:provider-not-linked` | 400 · `provider` | The account has no identity at that provider. |  |
| `api:auth:sso:resend-too-soon` | 400 · `pendingLink` | A confirmation e-mail was sent moments ago; wait before asking for another. | Wait a few minutes before asking for another. |
| `api:auth:sso:sign-up-closed` | Redirect | Signing up: sign-up isn't open. | Ask a tenant admin to invite you. |
| `api:auth:sso:unknown-provider` | 400 · `provider` | The provider named isn't one Maitento knows. |  |

## Notifications

The live notification feed (`GET /notifications`).

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:notifications:invalid-scope` | 400 · `scope` | The `scope` isn't `user` or `tenant`. |  |

## Cloud shell

Opening the portal's cloud shell.

| Code | Status | What it means | What to do |
|---|---|---|---|
| `api:shell:process-token-refused` | 403 | The cloud shell was asked for with a run's token. Only your own signed-in session can open it. |  |
| `api:shell:too-many-pending-tickets` | 400 · `ticket` | Too many cloud shell connections are being opened at once. | Wait a moment and try again. |
