API
Error catalog
Every ValorBrain error code with cause and action, generated from the engine catalog.
Generated from the credential matrix · version 1 · sha
9d2b630103a5. Do not edit by hand: the source issrc/credential-matrix.tsin the engine.
Match on the code, not the text. The code comes in the JSON error (or code) field, in an OAuth redirect's ?error= or in WWW-Authenticate's error=. An engine test guarantees every code the server returns is on this list.
Codes
| Code | HTTP | Surface | Cause | What to do |
|---|---|---|---|---|
missing_bearer | 401 | MCP | MCP request without Authorization: Bearer. | Send a vbm_ token or follow WWW-Authenticate into OAuth discovery. |
invalid_token | 401 | MCP, REST, SaaS ingest, OAuth | Unknown, revoked or expired token (or, on MCP, a secret with no prefix). | Create a new token in Settings → MCP Tokens or redo the signup/consent. |
wrong_credential_type | 401 | MCP | REST key (vb_, vb_agent_) or ingest key (fk_) sent to MCP. | Use the mcp_token (vbm_…) on MCP and keep the api_key for REST. |
agent_key_required | 401 | REST | The signup MCP token reissue (POST /api/v1/agents/mcp-token) got a credential other than the workspace's vb_agent_ key. | Call the route with the api_key (vb_agent_…) the signup returned. |
insufficient_scope | 403 | REST, SaaS ingest, MCP tool | The token is valid but lacks the scope the route or tool requires. | Issue a token with the scope named in required_scope (or write for ingest). |
credential_required | 403 | REST | Document purge without a bearer credential. | Send the tenant token; dry_run=true works without one. |
admin_required | 403 | REST | Operation that requires the tenant ADMIN role. | Repeat with a workspace administrator's token. |
attestation_required | 403 | REST | Class-A authority act without X-VB-Attestation. | Send a live interactive attestation envelope for this request. |
cap_human_forbidden | 400 | REST | Mandate with cap=human: human approval cannot be delegated. | Use a cap below human. |
identity_denied | 200 | MCP tool | The token has scoped_access and the tool asked for a collection outside its allow-list. | Ask the workspace owner to add the collection to the token. |
scope_denied | 200 | MCP tool | The tool requires a scope the MCP token lacks. | Issue a token with the tool's scope (Settings → MCP Tokens). |
outside_keyspace | 200, 403 | REST, MCP tool | An integration credential tried to write outside its keyspace: a fact key outside it, or any write that is not a keyed fact (document, message, state). Nothing was written. | Write only keyed facts (POST /api/v1/keyed-facts or upsert_keyed_fact) with keys in the keyspace; for another keyspace, ask the admin to widen the integration. |
profile_identity_unavailable | 200 | MCP tool | Plugin profile without a resolved user identity. | Connect with a token bound to a workspace user. |
mcp_init_failed | 403, 500 | MCP | The token belongs to another target (audience) or the MCP transport failed to start. | Use the token for the right instance; on 500, retry. |
internal_error | 500 | MCP | Unexpected failure while serving the MCP request. | Retry shortly; if it persists, open a ticket with the request time. |
invalid_tenant_header | 400 | REST | X-Tenant-ID is not a tenant UUID. | Drop the header (the tenant comes from the credential) or send a valid UUID. |
invalid_user_header | 400 | REST | X-User-ID is not a user UUID. | Drop the header or send the user's UUID. |
invalid_member_id | 400 | REST | member_id is not a member UUID. | Send the team member's UUID. |
not_document_owner | 403 | REST | POST /documents over another user's private document, or changing an owned document's sensitivity, without being the owner on their own credential, a tenant admin or the platform; also a foundation PUT/DELETE on another user's private document. Nothing was written. | Write with the owner's, a tenant admin's or the platform credential, or use another path. |
sensitivity_change_requires_admin | 403 | REST | POST /documents changing the sensitivity of a document with no owner: lowering it opens it to restricted tokens, raising it locks the team out. Nothing was written. | Ask a tenant admin, or write without changing the sensitivity. |
integration_not_minted_here | 400 | REST | POST /api/v1/mcp/tokens with credential_kind: integration: an integration credential is created with its mandate, on another route. Nothing was created. | Create the integration at POST /api/v1/authority/integrations, or ask for personal or gateway here. |
user_not_in_tenant | 400 | REST, OAuth | MCP token (or app OAuth consent) requested for a user_id that is not a tenant member. | Provision the user before issuing the token or approving the consent. |
email_already_registered | 409 | REST | The admin e-mail already exists in another tenant. | Sign in with the existing account. |
user_id_conflict | 409 | REST | The e-mail is already bound to another user id in the tenant. | Use the returned engine_user_id. |
rate_limited | 429 | OAuth | OAuth dynamic registration above the per-IP limit. | Wait for Retry-After and reuse the client_id you already registered. |
answer_burst_exceeded | 429 | REST | Too many answers for the tenant in a short window. | Wait for Retry-After. |
answer_cap_reached | 429 | REST | The customer's monthly answer cap was reached. | Raise or remove the cap in settings. |
not_found | 404, 200 | REST, MCP tool | Document, file or gap does not exist in this tenant. | Check the docid/path; similarFiles suggests close names. |
ambiguous | 409 | REST | The name matches more than one entity. | Pick one of the candidates and repeat with its id. |
ambiguous_docid | 400 | REST | The docid prefix matches more than one document. | Use a longer prefix. |
confirmation_required | 400 | REST | Whole-collection purge without confirm equal to the target. | Run dry_run=true, then repeat with confirm and an admin credential. |
superseded_document | 404 | REST | The docid is a replaced version. | Read read_at or the live document in superseded_by. |
supersedes_self | 400 | REST, MCP tool | supersedes points at the document being written. | Drop the target: rewriting the same path is already a revision. |
supersedes_invalid | 400 | REST, MCP tool | supersedes list too long or with a malformed target. | Send up to the maximum targets, each a docid or a path. |
supersedes_not_found | 400 | REST, MCP tool | A supersedes target does not exist. | Check the replaced document's docid or path. |
supersedes_ambiguous | 400 | REST, MCP tool | A supersedes target matches more than one document. | Use the full docid or the path. |
revision_conflict | 409 | REST | expected_revision is not the current revision. | Reread the document (current_revision) and reapply the change. |
update_failed | 200 | REST | One batch item could not be updated. | Retry only the items with ok: false. |
resolve_failed | 200 | MCP tool | The knowledge gap could not be marked resolved. | Retry shortly; if it persists, open a ticket with the request time. |
approval_required | 403, 200 | REST, MCP tool | Write with weaker authority than the winning value; it becomes a proposal. | Ask for human approval or correct with assert_authority_correction. |
pii_defense_block | 422 | REST, MCP tool | Write refused: high-risk PII that could not be vaulted. | Remove the sensitive data or store it with secrets. |
invalid_request | 400, 405 | OAuth | Missing or invalid parameter (PKCE S256, redirect_uri, form body). | Fix the request per error_description. |
invalid_client | 401, 400 | OAuth | Unknown client_id. | Register the client again at /oauth/register. |
invalid_client_metadata | 400 | OAuth | Invalid dynamic registration metadata. | Fix the /oauth/register body. |
invalid_redirect_uri | 400 | OAuth | redirect_uri not allowed (http only on loopback; dangerous schemes refused). | Use https, loopback or an app-specific scheme. |
invalid_grant | 400 | OAuth | Code expired, reused, from another client, wrong PKCE or invalid EMA assertion. | Restart the authorization flow. |
invalid_scope | 400, 302 | OAuth | Requested scope outside read write. | Ask for read, write or both. |
invalid_target | 400, 302 | OAuth | resource (RFC 8707) is not a resource of this issuer. | Use the MCP URL or omit resource. |
access_denied | 302 | OAuth | The person denied the consent. | Nothing to do; offer to try again. |
unsupported_grant_type | 400 | OAuth | grant_type other than authorization_code or jwt-bearer. | Use authorization_code with PKCE (or jwt-bearer for EMA). |
unsupported_response_type | 302 | OAuth | response_type other than code. | Use response_type=code. |
method_not_allowed | 405 | OAuth | OAuth endpoint called without POST. | Use POST. |
server_error | 500 | OAuth | Internal failure in the authorization server. | Retry shortly; if it persists, open a ticket with the request time. |
invalid_consent_session | 400 | OAuth | consent_session tampered with, signed by another key or malformed. | Start the connection again from the app (new /oauth/authorize). |
consent_session_expired | 400 | OAuth | The consent_session is past its 15 minutes. | Start the connection again from the app. |
consent_session_used | 409 | OAuth | This consent was already decided (approval or denial is single use). | Start the connection again from the app. |
tenant_inactive | 403 | OAuth | The session's workspace is not billing-active. | Restore the workspace subscription and approve again. |
unauthorized | 401 | OAuth | Internal consent endpoint called without the platform credential (only the SaaS calls it). | Clients do not call this endpoint: use /oauth/authorize. |
Answers without a code
Some gates still answer with a fixed message in the error field. The message is stable and can be compared literally.
| Code | HTTP | Surface | Cause | What to do |
|---|---|---|---|---|
Unauthorized | 401 | REST | REST without a bearer, or with a credential that is not for REST (fk_, admin secret). | Send Authorization: Bearer vbm_… (or the account's vb_agent_). |
Unauthorized: admin secret required | 401 | REST | Platform administration route. | Not a client route; use the public REST routes. |
Forbidden: admin credential required for money routes | 403 | REST | Credit or subscription route called with a tenant token. | Not a client route; change plans in the app. |
Forbidden: /metrics is served only to local scrapers | 403 | REST | /metrics from outside the local network without the metrics token. | Not a client route. |
Rate limit exceeded — please slow down | 429 | REST | Per-credential (or per-IP, on a public route) limit exceeded. | Wait retry_after_ms and slow down. |