Skip to main content
Version: 1.13.0-dev

Error Handling

All errors from DVARA use a consistent JSON envelope regardless of which surface returned them. Three separate services emit errors, and the tables below are grouped by source so that every row maps to behavior that actually runs:

  • Data plane errors — the DVARA LLM Gateway on port 8080, serving /v1/* and the actuator surface. The Enterprise build also exposes /actuator/license; the Open Source distribution does not.
  • Flightdeck errors — DVARA Flightdeck on port 8090, serving the Flightdeck.
  • MCP Gateway errors — served by the gateway on port 8080 alongside /v1/* (the separate MCP process was retired in 1.7.0). Approval-gate, loop-detection, and other agentic errors share the same envelope shape as the rest of the MCP errors below.

A single error code lives in exactly one of these three tables — there's no global "500-row catalog". If you can't find a code below, it means it isn't reachable on the surface you're looking at.

Error Envelope​

{
"error": {
"message": "Human-readable description of the error",
"type": "invalid_request_error | provider_error | gateway_error",
"code": "machine-readable error code",
"param": "field name, if validation error",
"trace_id": "the request's X-Trace-ID, or a 32-character hex ID"
}
}
FieldTypeDescription
messagestringHuman-readable error description
typestringError category (see table below)
codestringMachine-readable error code
paramstringField name that caused the error (validation only)
trace_idstringTrace ID for debugging: the request's X-Trace-ID if it sent a valid one (1–128 characters from A-Z a-z 0-9 . _ : -), otherwise a 32-character hex ID

Data plane errors​

Emitted by the DVARA LLM Gateway on port 8080 for every /v1/* request.

From 1.8.5, /v1/messages and /v1/messages/count_tokens give the same status and code in Anthropic's envelope instead: {"type":"error","error":{"type":...,"message":...,"code":...},"request_id":...}. There error.type is Anthropic's type for the status, such as authentication_error for 401 or permission_error for 403. See Use the Anthropic Messages API.

StatustypecodeWhen
400invalid_request_errorvalidation_errorMissing required field on the request DTO (model, messages). Emitted by request-body validation, before the gateway's exception path.
400invalid_request_errorno_providerNo provider on the route matches the requested model, or the matching provider has no credential configured.
400invalid_request_errorno_capable_providerThe route has providers but none of them support the requested response_format, tool calls, or tool calls on a stream; or no registered provider supports the Batch API.
400invalid_request_errorinvalid_requestGeneric request-shape rejection (missing response_format.type, etc.) after the body has parsed successfully.
400invalid_request_errorprovider_rejected_requestThe provider refused the request itself (upstream 400, 404, 413 or 422). The message carries the provider's reason. Not retried, and no failover: the same request fails the same way anywhere.
400invalid_request_errorinvalid_jsonMalformed JSON body — parsing failed before request-DTO binding. Emitted when the JSON body is malformed or unparseable.
400invalid_request_errorunsupported_response_formatDefensive provider-level check for response_format types the provider can't handle. In normal flow, capability-aware routing intercepts these requests first and returns no_capable_provider; this code surfaces only when routing bypasses capability filtering (for example, explicit model-prefix routing).
400invalid_request_errorunsupported_capabilityThe selected provider does not support the content in the request. Raised by Gemini, Bedrock and Ollama for an https image URL when image fetching is off. Before 1.10.0 Cohere and Groq also raised it for images and for tool-use / tool-result content blocks, and Mistral for images; from 1.10.0 those are sent to the provider, and a model that cannot take them answers with the provider's own error. Also raised for other unsupported request features, such as seed on Anthropic or Bedrock, input_audio or file content parts, or a tool type other than function and custom. Capability-aware routing does not pre-filter vision content on the primary route, so the provider itself rejects the request. Tool calls are pre-filtered; see no_capable_provider.
400invalid_request_errorprompt_experiment_not_runningThe named prompt experiment is not running, has no variants, or its variant weights do not total 100. Correct the experiment before retrying.
400invalid_request_errorunsupported_api_versionFrom 1.8.5, on /v1/messages: the anthropic-version header is missing or is not 2023-06-01. From 1.12.0 2026-10-21 is served too, and what that version removed (temperature, top_p, top_k, a document in an assistant message, citations on a user's text) is refused with invalid_request.
400context_window_errorcontext_window_exceededEstimated input tokens reach the workspace's hard threshold, 90 % of the model's context window by default (guardrail.context.hard-threshold-pct), and no pruning strategy is configured. The window is the model's model_limits entry in gateway.yaml, then the Enterprise model catalogue, else the provider default. In 1.8.5 the check used one limit per provider; see the known issue.
400pii_violationpii_detectedPII detected in the request and the workspace policy is BLOCK.
403pii_violationpii_tokenize_unavailableTOKENIZE was selected but this build has no reversible token store. The provider was not called.
403pii_violationpii_redact_unavailableA legacy v1 code retained for compatibility when Enterprise tokenization cannot obtain usable key or store capacity. Only TOKENIZE reaches it; the response includes attempted_action: "TOKENIZE".
402budget_exceededbudget_cap_hardHard budget cap reached on the workspace or the API key.
402budget_exceededper_call_cost_exceededA single request's estimated cost (max_tokens × the output price, or the reasoning price when that is higher, plus the input tokens × the input price) exceeded the per-call ceiling, set by dvara.llm-gateway.finops.per-call-max-cost-usd or Workspace.metadata["cost.per-call-max-usd"]. Complements the accumulated-spend cap; off by default.
403policy_violationpolicy_deniedRequest blocked by an active policy rule.
403guardrail_violationguardrail_blockedGuardrail detector flagged the request at or above the configured risk threshold and the workspace action is BLOCK.
403data_residency_errordata_residency_violationGeo-aware routing found no provider in the workspace's allowed regions, or, from 1.10.0, the provider failed and no fallback in them was left.
403ip_access_errorip_access_deniedClient IP blocked by global or per-workspace CIDR rule.
403workspace_credential_requiredworkspace_credential_requiredStrict-BYOK enforcement rejected the call — the workspace has no own active provider credential. Set under dvara.credentials.require-workspace-credential=true (global) or Workspace.metadata["credentials.require-workspace-credential"] (per-workspace). Audit event: PROVIDER_CREDENTIAL_MISSING.
403workspace_suspendedworkspace_suspendedWorkspace administratively suspended — trial-cliff expiry, chronic-abuse sweep, self-delete schedule, or manual operator action. Audit event per blocked request: WORKSPACE_SUSPENDED_BLOCK. When the workspace record holds a reason for the suspension, the error carries it as its own field, "reason" (for example "trial_expired"), so a client can tell one kind from another without reading the message; the field is absent when no reason was recorded. From 1.8.1 a suspension applies to the next request, and lifting one is served just as quickly; before that, either direction could take about a minute on the LLM plane.
403session_killedsession_killedFrom 1.8.5, Enterprise platform. The request's X-Session-Id names a killed session. The message is "Agent session has been killed: <id>. Start a new session." It is 403, not 429, so clients do not retry. A kill lasts 24 hours. GET /v1/models and calls with no session id are not checked. See Stop a session.
413input_size_errorinput_too_largeRequest exceeds guardrail.max-input-tokens, guardrail.max-messages-per-request, or guardrail.max-message-length.
422schema_validation_errorschema_validation_failedThe response failed output-schema validation. DVARA does not retry this failure automatically.
429rate_limit_errorrate_limit_exceededPer-key request count or token budget exhausted in the current 60-second window. Emitted directly by the rate-limit filter.
429priority_throttle_errorpriority_throttledPriority admission controller rejected the request because the workspace's tier is over its concurrency threshold.
429provider_rate_limitedprovider_rate_limitedThe provider itself answered 429, or the upstream provider credential's published rate-limit budget is exhausted and the gateway shed the request after finding no capable non-shed fallback. A shed carries a Retry-After header derived from the upstream signal and requires dvara.llm-gateway.provider-rate-limit.enabled=true.
502provider_errorprovider_response_too_largeFrom 1.8.5. The provider's response passed dvara.llm-gateway.response-limits.max-body-bytes (64 MiB) or, for a stream or file download, max-stream-bytes (128 MiB). Refused before it is parsed; not retried, and not counted against the provider's circuit breaker.
502provider_errorprovider_errorUpstream provider returned a 5xx, 401 or 403 (or another error status not listed above), the connection failed, or a stream ended without a finish reason, had an empty body, or carried an in-band error. Provider-specific detail is prefixed to the message. An upstream 400/404/413/422 is provider_rejected_request, and an upstream 429 is provider_rate_limited.
503provider_unavailableprovider_circuit_openThe provider's circuit breaker is OPEN; no upstream call was attempted.
503workspace_credential_unavailableworkspace_credential_unavailableA workspace credential exists but cannot be decrypted or resolved, or its store cannot be read. DVARA does not fall back and emits PROVIDER_CREDENTIAL_UNAVAILABLE. Enterprise platform.
503provider_unavailablefailover_capability_mismatchPrimary provider failed and no capable fallback exists. Response also carries the X-Gateway-Failover-Blocked: capability_mismatch header.

Streaming can fail after the HTTP 200 and SSE headers have been sent. In that case the status cannot change. A final content-filter event ends delivery and the audit trail records STREAM_TOO_LARGE_TO_SCAN, STREAM_TOOL_ARGUMENTS_UNENFORCEABLE, or STREAM_INCOMPLETE_UNGROUNDED. Read STREAMING_ENFORCEMENT_SUMMARY for the separate scan_incomplete, scan_failed, and truncated facts. Do not retry blindly: reduce an over-limit response, fix malformed tool arguments, or retry a truncated upstream only when the operation is safe to repeat.

Prompt-template codes (data plane)​

Emitted when a request references a template via metadata.prompt_template_id and the resolver cannot satisfy the reference.

StatustypecodeWhen
400invalid_request_errorprompt_template_not_activeReferenced template is not in ACTIVE status.
400invalid_request_errorprompt_variable_missingRequired {{variable}} not provided in metadata.prompt_variables.
404not_found_errorprompt_template_not_foundThe template id is unknown, or it belongs to another workspace.

DVARA Open Source resolves active templates from gateway.yaml, but it does not run prompt experiments. If a request supplies metadata.prompt_experiment_id, the Open Source data plane returns HTTP 400 with code: unsupported_capability; it does not ignore the experiment and send the call as ordinary traffic. Prompt experiments require the Enterprise platform.

Additional guardrail error codes​

StatustypecodeWhen
403guardrail_violationhallucination_detectedThe grounding-detection layer fires with action: BLOCK — response contains ungrounded claims.
500guardrail_plugin_errorguardrail_plugin_errorAn external guardrail plugin configured with fail-mode: CLOSED failed to complete its call or returned a malformed 200, or the semantic guard with fail-mode: CLOSED could not evaluate (from 1.10.0).

Flightdeck errors​

Flightdeck, on port 8090, is a web application. It has no error envelope and no list of error codes for a caller to branch on. When it refuses a change, it says why in words on the page: a message on the form, which keeps what you typed, or a notice at the top of the page.

The *_not_found, invalid_* and *_duplicate codes that used to be listed here belonged to the Admin API, which was removed in 1.7.0. Two /v1/admin paths remain on Flightdeck: POST /v1/admin/config/import, for a GitOps import from a script, and GET /v1/admin/config/bundle/signing-key. Any other /v1/admin/** path returns 404.

A new A2A agent ID may not contain ::, which DVARA uses to join an agent to a skill. The Flightdeck form and a GitOps import both refuse such an ID and name it. Use -, _ or . instead. An agent that already has such an ID keeps it.

Authentication and authorization​

Requests to /v1/*, /internal/* and /actuator/* get these JSON bodies. The codes are upper case. Every other Flightdeck path answers a refused request with a plain 401 or 403 error response, whatever the caller.

StatusBodyWhen
401{"error":{"code":"UNAUTHORIZED","message":"Authentication required","type":"authentication_error"}}Missing or invalid bearer token, PAT or JWT.
403{"error":{"code":"FORBIDDEN","message":"Insufficient permissions","type":"authorization_error","method":"…","path":"…"}}The caller lacks the role the path needs. Audited as AUTHORIZATION_DENIED with the method, path and principal.

MCP Gateway errors​

Emitted by the MCP plane, which runs inside the gateway on port 8080 under /mcp. Almost every error — auth, policy, agentic governance, upstream failure, workspace suspension — comes back through the same lowercase-code, type: "mcp_error" envelope.

{
"error": {
"message": "…",
"type": "mcp_error",
"code": "mcp_server_not_found",
"trace_id": "…"
}
}
Native MCP request body failures

From 1.8.6, native POST /mcp limits the complete request body with dvara.mcp-gateway.limits.max-request-bytes, 4 MiB by default. A larger declared or chunked body receives a bare HTTP 413 before JSON-RPC processing, so it has no DVARA error envelope or code. Authentication runs first; an invalid credential still receives 401 without its body being read. This outer limit does not cover the server-scoped MCP REST bridge.

StatuscodeWhen
400mcp_not_availableThe MCP implementation needed for this operation is not available in this build. Do not use this code to infer licence state.
400mcp_upstream_rejectedFrom 1.8.4. A native MCP server answered the call with a JSON-RPC invalid request, method not found or invalid params error, such as an unknown tool. Fix the call; a retry will fail the same way. error.upstream_error carries the server's code, message and data.
410mcp_transport_sunsetFrom 1.8.5. The MCP server uses the legacy HTTP+SSE transport and its sunset date has passed (2027-07-28 by default, set by dvara.mcp-gateway.legacy-sse-sunset). Move the server to Streamable HTTP.
413mcp_arguments_too_largeFrom 1.8.5. The tool call's arguments, as JSON, are larger than dvara.mcp-gateway.limits.max-arguments-bytes (1 MiB by default) or the workspace's own limit. Refused before the schema check, policy, the PII scan and the server.
400mcp_request_state_invalidFrom 1.8.5. The client's requestState for an input round trip is not a string or is larger than 16 KiB. The same code comes back as 502 when the server sent such a state.
400mcp_pii_detectedPII detected in the tool-call arguments or response, workspace policy is BLOCK.
401mcp_auth_requiredMissing Authorization: Bearer <key> on the MCP Gateway request.
401mcp_auth_invalidAPI key does not resolve to an active workspace.
401mcp_auth_revokedAPI key has been revoked.
403mcp_policy_deniedMCP request denied by a policy rule.
403mcp_approval_deniedA human reviewer denied the tool call from the DVARA Flightdeck approval queue.
403mcp_approval_unavailableFrom 1.8.4. The tool call needed approval, the approval store could not be reached, and the workspace's default approval action is deny. No reviewer could act. Before 1.8.4 this was a 502, which clients retry.
403mcp_workspace_suspendedThe calling workspace is suspended. Since 1.7.0 each plane checks workspace state before forwarding, so suspension stops LLM requests, MCP tool calls, and A2A hops. The body is {"error":{"message":"Workspace is suspended","type":"mcp_error","code":"mcp_workspace_suspended","trace_id":"…"}}.
403mcp_session_killedThe session has been killed; later tool calls for that session are refused at once. From 1.8.5 a kill made on any plane, or by loop detection, counts, and it lasts 24 hours. A call held for approval when the kill lands is refused too.
403mcp_sampling_refusedFrom 1.8.5. The server answered with a request for the client to run a model call (sampling/createMessage). DVARA never relays it, because that model call would skip the LLM Gateway's governance.
403mcp_input_request_refusedFrom 1.8.5. The server asked for input the workspace does not allow, such as a roots request (refused by default) or an elicitation the workspace turned off.
404not_found (type not_found_error)Wrong URL under /mcp (e.g. typo, excluded actuator path, bot probe). Measured on 1.7.0 — a path no controller claims is answered with the neutral envelope, not the mcp_error one. Returned when the route or path is unknown so probes to non-existent paths don't masquerade as 500 mcp_internal_error.
404mcp_server_not_foundMCP server ID not registered for the calling workspace.
408mcp_approval_timeoutApproval gate reached its configured timeout with no human decision; workspace default action is deny.
429mcp_rate_limit_exceededPer-key rate limit exhausted.
429mcp_agent_loop_detectedThe loop detector fired (repetition threshold, cycle detection, or rate cap) on the session.
503mcp_injection_scan_failedA tool response could not be scanned and the workspace injection action is BLOCK or SANITIZE; FLAG records the failure and returns the response.
502mcp_response_too_largeFrom 1.8.5. The tool result passed dvara.mcp-gateway.response-limits.max-body-bytes (4 MiB) or, for a stream, max-stream-bytes (16 MiB), or the workspace's own limit. Refused before it is parsed and not retried.
502mcp_upstream_errorFrom 1.8.4. A native MCP server answered the call with any other JSON-RPC error. error.upstream_error carries the server's code, message and data.
502mcp_server_unavailableA native MCP server could not be reached: its address did not resolve, it refused the connection, it did not answer in time, or its session could not start. The message names which. From 1.9.0 a STDIO server whose process exits during a call fails that call at once with this code, on every protocol version, instead of after a 30-second wait; the next call starts the server again. From 1.8.4 a server that answers the call with a JSON-RPC error is no longer reported this way; see the two codes above.
502(fall-through)Any other gateway-thrown error code; the MCP Gateway lowercases it, prefixes mcp_ if needed, and returns 502 mcp_error / mcp_<code>.
503mcp_server_unavailableThe MCP server is registered but not active, for example suspended, disabled or quarantined; the message names the state.
500mcp_internal_errorAny unhandled exception — caught by the generic handler branch.

From 1.8.4 an upstream JSON-RPC error comes back as the server gave it. A call to an unknown tool on POST /mcp/{serverId}/tools/call returns:

{
"error": {
"message": "The MCP server answered with an error: Unknown tool: search_artcles",
"type": "mcp_error",
"code": "mcp_upstream_rejected",
"upstream_error": {
"code": -32602,
"message": "Unknown tool: search_artcles"
},
"trace_id": "a6783439db1f46a6bfed511a0011e955"
}
}

upstream_error.data is present when the server sent data. The pooled session to the server is kept.

The native JSON-RPC /mcp endpoint represents governance refusals with application-specific JSON-RPC codes rather than this HTTP envelope. See the native MCP refusal table.

Error Examples​

Validation Error — Missing Model​

curl -X POST http://localhost:8080/v1/chat/completions \
-H "Authorization: Bearer $DVARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"messages": [{"role": "user", "content": "Hi"}]}'
{
"error": {
"message": "model is required",
"type": "invalid_request_error",
"code": "validation_error",
"param": "model",
"trace_id": "a6783439db1f46a6bfed511a0011e955"
}
}

No Provider Configured​

{
"error": {
"message": "No provider configured for model: gpt-4o. Set OPENAI_API_KEY to register the provider.",
"type": "invalid_request_error",
"code": "no_provider",
"trace_id": "d354d2faaa5f4e14939aa8c480fb9d90"
}
}

Unsupported Response Format​

{
"error": {
"message": "Ollama provider does not support response_format json_schema. Supported formats: [text, json_object]",
"type": "invalid_request_error",
"code": "unsupported_response_format"
}
}

Credential Not Found​

{
"error": {
"message": "Required credential not found: provider.openai.api-key (platform default)",
"type": "not_found_error",
"code": "credential_not_found",
"trace_id": "f1234567890abcdef1234567890abcde"
}
}

Rate Limit Exceeded​

HTTP/1.1 429 Too Many Requests
Retry-After: 1
X-Trace-ID: a6783439db1f46a6bfed511a0011e955
{
"error": {
"message": "Rate limit exceeded",
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"trace_id": "a6783439db1f46a6bfed511a0011e955"
}
}

Provider Error​

Error messages include a human-readable hint for common HTTP status codes:

{
"error": {
"message": "Anthropic API error 401 (invalid API key - check your provider credentials)",
"type": "provider_error",
"code": "provider_error",
"trace_id": "b7894561cd2e4f38a1cc820d1f1f5044"
}
}

Hints are appended for these status codes:

StatusHint
401invalid API key - check your provider credentials
403access denied - your API key may lack required permissions
404model or endpoint not found - check the model name
429rate limited by provider - too many requests
500provider internal server error
502provider returned bad gateway
503provider is temporarily unavailable

Circuit Breaker Open​

When a provider fails repeatedly, the gateway stops sending requests to it temporarily to avoid cascading failures. The error message explains what happened and what to do:

{
"error": {
"message": "Provider anthropic is temporarily unavailable — too many recent requests failed, so new requests are paused. This usually resolves automatically after a short cooldown. If it persists, check your provider API key and account status.",
"type": "provider_unavailable",
"code": "provider_circuit_open",
"trace_id": "c8905672de3f5049b2dd931e20206155"
}
}

PII Detected (Block Mode)​

{
"error": {
"message": "Request blocked: PII detected (EMAIL, SSN)",
"type": "pii_violation",
"code": "pii_detected",
"trace_id": "00127894ab5c6d71e4ff153f42428377"
}
}

Guardrail Blocked​

{
"error": {
"message": "Request blocked: guardrail violation detected (INJECTION, JAILBREAK)",
"type": "guardrail_violation",
"code": "guardrail_blocked",
"trace_id": "11238905bc6d7e82f500264053539488"
}
}

Input Too Large​

{
"error": {
"message": "Request exceeds maximum messages limit: 150 > 100",
"type": "input_size_error",
"code": "input_too_large",
"trace_id": "22349016cd7e8f930611375164640599"
}
}

Schema Validation Failed​

{
"error": {
"message": "Output schema validation failed: $.name: required property missing",
"type": "schema_validation_error",
"code": "schema_validation_failed",
"trace_id": "33450127de8f90041722486275751600"
}
}

Context Window Exceeded​

{
"error": {
"message": "Request exceeds context window: 150000 tokens estimated, max 128000",
"type": "context_window_error",
"code": "context_window_exceeded",
"trace_id": "44561238ef9001152833597386862711"
}
}

Failover Blocked​

HTTP/1.1 503 Service Unavailable
X-Gateway-Failover-Blocked: capability_mismatch
{
"error": {
"code": "failover_capability_mismatch",
"message": "Failover blocked: no fallback provider supports response_format: json_schema. Primary provider [openai] failed: upstream down"
}
}

Licence limits​

An Enterprise install without a licence may make 100,000 governed calls a month: LLM calls, MCP tool calls and A2A hops counted together. Once the month's count reaches 110,000, each plane refuses the call with 402 and its own code. On /v1/*:

HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"error": {
"code": "license_token_cap_exceeded",
"type": "token_cap_exceeded",
"message": "Without a licence this install can make 100,000 calls a month, counting LLM calls, MCP tool calls and A2A hops together. This month: 110,000. Calls resume on the 1st, or when the platform owner adds a licence in Flightdeck.",
"tier": "unlicensed",
"current_usage": 110000,
"cap": 100000,
"upgrade_url": "https://dvarahq.com/pricing"
}
}

/mcp/* answers 402 with code: mcp_cap_exceeded, and /a2a/* with code: a2a_cap_exceeded and type: a2a_error, with the same message. A refused call is not counted. Calls resume on the 1st of the month, or on the next request after the platform owner adds a licence in DVARA Flightdeck. No restart is needed.

A valid licence has no call limit. From 1.8.6 it counts calls without refusing any, for the monthly usage report. An expired licence counts calls but never refuses one. Until 1.8.2, an expired licence refused /mcp/* and /a2a/* with 402 LICENSE_EXPIRED; that response no longer exists. Check where the install stands with Enterprise GET /actuator/license and DVARA_ACTUATOR_API_KEY. See Get and apply a license.

A2A Gateway errors​

Enterprise only

Not included in DVARA Open Source.

Where the code is, from 1.9.0. On a JSON-RPC call to /a2a/*, error.code is a number, as the A2A specification requires: the specification's own where it names the error, and -32000 for a refusal by DVARA. The code in this table is in error.data.code with A2A 0.3 (or no version), and in error.data[0].metadata.code with A2A 1.0, where error.data[0] is a google.rpc.ErrorInfo whose domain is dvarahq.com for a refusal by DVARA and a2a-protocol.org for an error the specification defines. Before 1.9.0 the code was the text of error.code. A stream's error event has the same form. The Agent Card request keeps a plain HTTP error (a google.rpc.Status for an A2A 1.0 client). See Read a refusal.

The status, from 1.9.0. A refusal of a JSON-RPC call (a POST to /a2a/*) is answered with HTTP 200, as the official A2A SDK's server answers it, so an SDK client can read it. A missing, expired or revoked key is the exception: it keeps 401. The status in the table below is the refusal's own: DVARA sends it only when the operator sets a2a.protocol.json-rpc-error-status: refusal, and it is what the hop history shows. An Agent Card request, a request that is not a POST, and a 415 for a body that is not JSON keep their status. See The HTTP status of a refusal.

StatusCodeMeaning
400a2a_not_availableThe A2A implementation is unavailable in this build.
400a2a_pii_detectedThe configured PII action refused the message.
413a2a_message_too_largeAn outbound A2A message carries more text than a2a.limits.max-message-length (50,000 characters by default). Checked before policy and the PII scan on message:send and message:stream.
400a2a_version_not_supportedFrom 1.9.0. The request named an A2A-Version DVARA does not serve; it serves 0.3 and 1.0 (0.3 only if the operator turned 1.0 off). The body is the A2A VersionNotSupportedError: JSON-RPC code -32009, with this code in error.data.code and the served versions in error.data.supportedVersions (a version that does not start 0. gets them in the google.rpc.ErrorInfo metadata, the versions as 0.3,1.0). Send 0.3, 1.0, or no version. See Name the A2A version.
400a2a_parse_errorFrom 1.9.0. The request body is not JSON (JSON-RPC code -32700). Before, this was a2a_invalid_request.
400a2a_invalid_requestThe request body is not a JSON-RPC request, or a request to POST /a2a/{agentId} names no method (JSON-RPC code -32600).
404a2a_method_not_foundFrom 1.9.0. The JSON-RPC method sent to POST /a2a/{agentId} is not an A2A method in the version of the request, for example SendMessage (an A2A 1.0 name) in a 0.3 request. JSON-RPC code -32601; the message names the right method. See Name the method.
400a2a_unsupported_operationFrom 1.9.0. A method DVARA knows but does not serve on the JSON-RPC address, such as GetExtendedAgentCard: read the Agent Card at GET /a2a/{agentId}/.well-known/agent-card.json instead. JSON-RPC code -32004; the message gives the reason.
400a2a_invalid_paramsFrom 1.9.0. The request is not valid A2A in its version: a message part that is not exactly one known kind, a member the version does not define, or a value such as "role": "user" in a 1.0 request. JSON-RPC code -32602; the message names the place and the reason, for example params.message.parts[1]: unknown member image. Nothing is sent to the peer. See Send A2A 1.0 messages.
400a2a_untranslatableFrom 1.9.0. Something in the request cannot be carried in the A2A version DVARA uses for the peer, such as a filename on a text part for a 0.3 peer, or ListTasks to a 0.3 peer. DVARA refuses rather than drop part of the message.
502a2a_upstream_invalid_responseFrom 1.9.0. The peer answered in a shape DVARA cannot read in the peer's A2A version. The answer is not passed on; on a stream, one error event ends the stream.
502a2a_upstream_untranslatableFrom 1.9.0. The peer's answer cannot be carried in your A2A version, such as a second page of push configs for a 0.3 client.
401a2a_auth_required, a2a_auth_invalid, a2a_auth_revokedThe plane key is missing, invalid, or revoked.
401a2a_delegated_token_requiredA delegated agent call lacks its delegated token.
402a2a_cap_exceededThe monthly call limit is used up: the install-wide limit of an install without a licence (see Licence limits), or a hosted plan's A2A band.
403a2a_policy_denied, a2a_scope_denied, a2a_workspace_suspendedPolicy, scope, or workspace posture refused the call.
403a2a_session_killedThe session is on the workspace deny list. From 1.8.5 a kill made on any plane counts and lasts 24 hours. A running stream ends with an a2a_session_killed error event, and a hop held for approval is refused.
403a2a_delegation_chain_required, a2a_delegation_chain_invalidThe delegation guard refused a hop with no chain from an agent's key, or a chain that was changed, expired, or belongs to another workspace, session or agent.
403a2a_delegation_scope_widenedFrom 1.9.0. A signed delegation chain sent in X-Dvara-Delegated-Authorization: DvaraDeleg … has a step that grants more than the step before it: an agent or skill, or an audience, its parent never had. The hop is not forwarded. reason is scope-widened.
403a2a_delegation_expiredFrom 1.9.0. A step of the signed delegation chain is outside its time window, or its window is longer than the gateway allows (a2a.delegation.max-token-lifetime-seconds, 15 minutes by default; a workspace can set a shorter limit). Ask the caller for a fresh chain. reason is expired.
403a2a_delegation_invalidFrom 1.9.0. The signed delegation chain did not verify, or does not allow this hop. reason says which check failed: malformed, unsupported, unresolved-key (no trusted key for a signer), bad-sig, broken-chain, wrong-audience (the chain may not be shown to this agent), agent-binding (the chain was given to another agent than the API key acts as), or not-granted (it does not grant this skill on this agent). Also wrong-workspace: a version 2 chain names another workspace than the one the call is in; and revoked: a step of the chain, or the agent that signed it, was withdrawn in Flightdeck. unverified: the workspace requires a signed chain, and the header held a plain bearer token. version-not-allowed: the workspace accepts only chains tied to it (version 2), and the chain is version 1.
403a2a_approval_denied, a2a_approval_unavailableThe required human approval was denied or unavailable.
404a2a_agent_not_foundThe agent is absent or outside the caller's workspace.
415a2a_unsupported_media_typeFrom 1.9.0. The request body was not application/json or application/a2a+json. The Accept header of the answer names the two types. See Choose the content type.
408a2a_approval_timeoutThe approval window expired.
409a2a_delegation_cycle, a2a_delegation_depth_exceededThe delegation guard refused a hop that loops back or makes the chain too deep. Do not retry.
409a2a_delegation_hop_budget_exceededFrom 1.8.5. The session has forwarded as many hops as a2a.delegation.max-hops-per-session allows. Do not retry.
429a2a_loop_detectedLoop detection stopped the agent chain.
429a2a_session_rate_limitedFrom 1.8.5. The session passed the hop rate (60 a minute). Unlike a loop, the session is not killed.
502a2a_peer_version_not_supportedFrom 1.9.0. The peer agent answered that it does not support the A2A version DVARA sent it. The message names the peer. On a stream it arrives as one error event. Check which A2A versions the peer serves.
502a2a_peer_protocol_unsupportedFrom 1.9.0. The peer's Agent Card offers no way to call it that DVARA can use: no JSON-RPC entry at an A2A version DVARA serves (today 0.3). Nothing is sent to the peer. The message lists what the peer offers. Ask the peer to list a JSON-RPC 0.3 interface. See How DVARA talks to each peer.
502a2a_peer_url_refusedFrom 1.9.0. The address on the peer's Agent Card failed a check that the registered endpoint passes: it is on another host, port or scheme, it is not a plain web address, or the card's signature does not allow it. Nothing is sent to the peer, not even at the registered endpoint. The message names the check. Register the address the card lists, or ask the peer to list the registered endpoint. See Which address DVARA calls.
502a2a_upstream_content_typeFrom 1.9.0. The peer agent answered with a content type DVARA cannot read, such as an HTML page, where it expected JSON or an event stream. The message names the type. On a stream it arrives as one error event. Check the peer's endpoint URL and that it is an A2A agent.
502a2a_endpoint_not_httpsFrom 1.8.5. The agent's endpoint is not https://. DVARA does not contact the peer. Move the agent to https:// or disable it.
502a2a_response_too_largeFrom 1.8.5. The peer's answer passed a2a.response-limits.max-body-bytes (16 MiB) or, for a stream, max-stream-bytes (64 MiB). Not retried; a stream is cut and ends with an error event.
502a2a_card_unavailable, a2a_card_signature_invalidAgent Card retrieval or signature verification failed. From 1.8.4 this includes a peer's authenticated extended Agent Card that its public card declares but that could not be fetched; DVARA does not fall back to the public card.
503a2a_agent_unavailableThe registered agent cannot serve traffic.
503a2a_delegation_binding_unavailableSince 1.8.4: the gateway could not read which agent the API key acts as, in a workspace that requires a chain from agents, or (from 1.9.0) for a signed delegation chain, with reason binding-unavailable. A retry may pass.
503a2a_delegation_settings_unavailableFrom 1.8.5. The workspace's delegation settings could not be read and this gateway has never read them. The hop is not forwarded or counted. A retry may pass.
503a2a_delegation_hop_budget_unavailableFrom 1.8.5, under the hardened preset only. The session's hop count could not be read in time. A retry may pass.
503a2a_delegation_hop_budget_below_quorumFrom 1.9.0, under the hardened preset only. The gateway's cluster is smaller than the minimum size the operator set for the session hop count. A retry may pass once the gateways are back.
403a2a_delegation_root_key_onwardFrom 1.9.0. A root key (one that may start chains) sent a hop that carries a delegation chain, and the workspace keeps root keys to starting work. Use the agent's ordinary key.
503a2a_push_not_availableFrom 1.8.5. A push config was sent, but the gateway has no A2A_PUSH_CALLBACK_BASE_URL, and the workspace has not chosen direct delivery.

The reason of a refused delegation chain, from 1.9.0. The three a2a_delegation_* codes for a signed chain name a group; the check that failed is next to the code, so a client can act on it without reading the message: in error.data.reason with A2A 0.3, and in error.data[0].metadata.reason with A2A 1.0. It is there on message:send, on a stream's error event and on task calls. For example, at 1.0:

{"jsonrpc": "2.0", "id": "1", "error": {"code": -32000,
"message": "Delegation chain refused (bad-sig): a block's signature does not verify",
"data": [{"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "A2A_DELEGATION_INVALID", "domain": "dvarahq.com",
"metadata": {"code": "a2a_delegation_invalid", "traceId": "…", "reason": "bad-sig"}}]}}

Flightdeck shows the same on the A2A hop pages: a refused chain with its reason, and on the workspace's A2A audit page a verified chain with its depth and how it narrowed from step to step.

From 1.8.5 a peer that posts a push notification to the gateway gets 413 when the notification is larger than a2a.push.max-body-bytes, and 502 when the gateway could not deliver it to the workspace's webhook. The peer can send it again.

An unmapped A2A code returns HTTP 502. DVARA preserves an existing a2a_ prefix or adds it before returning the lowercased code.

Additional canonical codes​

StatusCodeDistributionMeaning
400invalid_pathBothThe data-plane path is not valid.
401api_key_required, invalid_api_key, api_key_revoked, api_key_expiredBothThe data-plane credential is missing or unusable.
402license_token_cap_exceededEnterpriseThe monthly call limit is used up on /v1/*. See Licence limits.
402mcp_cap_exceeded, a2a_cap_exceededEnterpriseThe monthly call limit is used up on /mcp/* or /a2a/*. See Licence limits.
403api_key_scopeBothA valid key lacks the scope for this operation.
403grounding_unavailableEnterpriseGrounding was required but its evidence or engine was unavailable.
403mcp_scope_denied, a2a_scope_deniedEnterpriseA plane key lacks the required scope.
404batch_not_foundBothThe batch is absent from this workspace.
404file_not_foundBothThe file is absent or belongs to another workspace; both cases deliberately look the same.
503workspace_credential_unavailableEnterpriseA stored workspace credential exists but cannot be used; DVARA does not fall back.

Troubleshooting​

SymptomLikely CauseFix
400 no_providerAPI key not set for the providerSet the environment variable (e.g., OPENAI_API_KEY)
403 data_residency_violationData residency policy violationConfigure routing to use providers in allowed regions
400 unsupported_response_formatProvider doesn't support the formatUse a different provider or remove response_format
400 no_capable_providerNo provider on the route supports itAdd a capable provider to the route
400 pii_detectedPII found and action is BLOCKRemove the value, use irreversible REDACT, or use LOG while calibrating
403 pii_tokenize_unavailableThis build has no reversible token storeUse REDACT, or run the Enterprise tokenization prerequisites
403 pii_redact_unavailableEnterprise TOKENIZE cannot mint a tokenRestore its token store or key path; inspect attempted_action, then retry
403 guardrail_blockedInjection/jailbreak/content detectedReview prompt content; change workspace guardrail action to FLAG or LOG
413 input_too_largeRequest exceeds input size limitsReduce message count, message length, or total input tokens; increase workspace limits via metadata
422 schema_validation_failedResponse doesn't match JSON schemaReview the registered schema via the Flightdeck and tighten or relax it. DVARA validates once and does not retry.
400 context_window_exceededInput at or over 90 % of the window, no pruningReduce conversation length; configure pruning strategy via workspace metadata (guardrail.context.pruning-strategy)
402 budget_cap_hardHard budget limit exceededWait for the next budget period or raise the cap in Flightdeck
400 compliance_not_availableCompliance reporting is not available in this buildRun an Enterprise build that includes reporting
400 mcp_not_availableThe requested MCP implementation is not available in this buildRun a build that includes that MCP capability
401 mcp_auth_requiredMissing Bearer token on MCP GatewayInclude Authorization: Bearer gw_<key> header
401 mcp_auth_invalidInvalid API key on MCP GatewayVerify the API key is correct
401 mcp_auth_revokedAPI key revoked on MCP GatewayGenerate a new API key
404 mcp_server_not_foundMCP server ID does not existVerify server ID via the Flightdeck
403 mcp_policy_deniedMCP request denied by policyCheck policy rules via the Flightdeck. Review mcp_server, mcp_tool, mcp_arg conditions. In 1.12.0 an mcp_arg condition never sees the tool's arguments on live traffic, so a rule using it matches every call to that tool.
400 mcp_upstream_rejectedThe MCP server refused the call, e.g. unknown tool or bad argumentsRead error.upstream_error.message; fix the tool name or arguments. Do not retry unchanged.
502 mcp_upstream_errorThe MCP server answered with a JSON-RPC errorRead error.upstream_error and the server's own logs for the trace_id
502 mcp_server_unavailableMCP server unreachable, timed out, or its session did not startCheck the server's address and health; the call can be retried
403 mcp_approval_unavailableApproval store unreachable, default action denyRestore Flightdeck and its approval store; retrying before then fails the same way
503 mcp_server_unavailableMCP server suspended/disabledRe-activate the server or check its upstream health in Flightdeck
429 rate_limit_exceededToo many requestsWait for Retry-After seconds, or increase limits
502 provider_errorUpstream provider returned an errorRead the hint in the error message (e.g., "invalid API key"). For 401: check your provider API key env var. For 5xx: check the provider's status page. The gateway retries automatically. A provider 429 arrives as 429 provider_rate_limited instead: reduce the request rate.
400 provider_rejected_requestThe provider refused the request (unknown model, bad field, too large)Read the provider's reason in the message and fix the request. Retrying unchanged fails the same way.
503 provider_circuit_openToo many recent provider failuresThe gateway pauses requests to a failing provider to prevent cascading errors. It resolves automatically after a short cooldown. If it persists after restarting: check your provider API key and account status.
503 failover_capability_mismatchPrimary failed, no capable fallbackAdd more providers to the route that support the capability