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
8080alongside/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": "32-character hex trace ID"
}
}
| Field | Type | Description |
|---|---|---|
message | string | Human-readable error description |
type | string | Error category (see table below) |
code | string | Machine-readable error code |
param | string | Field name that caused the error (validation only) |
trace_id | string | 32-character hex trace ID for debugging |
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.
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | validation_error | Missing required field on the request DTO (model, messages). Emitted by request-body validation, before the gateway's exception path. |
| 400 | invalid_request_error | no_provider | No provider on the route matches the requested model, or the matching provider has no credential configured. |
| 400 | invalid_request_error | no_capable_provider | The route has providers but none of them support the requested response_format. |
| 400 | invalid_request_error | invalid_request | Generic request-shape rejection (missing response_format.type, etc.) after the body has parsed successfully. |
| 400 | invalid_request_error | invalid_json | Malformed JSON body — parsing failed before request-DTO binding. Emitted when the JSON body is malformed or unparseable. |
| 400 | invalid_request_error | unsupported_response_format | Defensive 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). |
| 400 | invalid_request_error | unsupported_capability | The selected provider does not support the content in the request. Raised by Cohere and Groq for image content (vision) and for tool-use / tool-result content blocks, and by Gemini, Bedrock and Ollama for an https image URL when image fetching is off. Capability-aware routing does not pre-filter vision or tool-call content, so the provider itself rejects the request. |
| 400 | invalid_request_error | prompt_experiment_not_running | The named prompt experiment is not running, has no variants, or its variant weights do not total 100. Correct the experiment before retrying. |
| 400 | invalid_request_error | unsupported_api_version | From 1.8.5, on /v1/messages: the anthropic-version header is missing or is not 2023-06-01. |
| 400 | context_window_error | context_window_exceeded | Estimated input tokens exceed the provider's context window and no workspace pruning strategy is configured. In 1.8.5 the check uses one limit per provider, not the model's own window; see the known issue. |
| 400 | pii_violation | pii_detected | PII detected in the request and the workspace policy is BLOCK. |
| 403 | pii_violation | pii_tokenize_unavailable | TOKENIZE was selected but this build has no reversible token store. The provider was not called. |
| 403 | pii_violation | pii_redact_unavailable | A legacy v1 code retained for compatibility when Enterprise tokenization cannot obtain usable key or store capacity. Only TOKENIZE reaches it in 1.8; the response includes attempted_action: "TOKENIZE". |
| 402 | budget_exceeded | budget_cap_hard | Hard budget cap reached on the workspace or the API key. |
| 402 | budget_exceeded | per_call_cost_exceeded | A single request's estimated cost (max_tokens × output 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. |
| 403 | policy_violation | policy_denied | Request blocked by an active policy rule. |
| 403 | guardrail_violation | guardrail_blocked | Guardrail detector flagged the request at or above the configured risk threshold and the workspace action is BLOCK. |
| 403 | data_residency_error | data_residency_violation | Geo-aware routing found no provider in the workspace's allowed regions. |
| 403 | ip_access_error | ip_access_denied | Client IP blocked by global or per-workspace CIDR rule. |
| 403 | workspace_credential_required | workspace_credential_required | Strict-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. |
| 403 | workspace_suspended | workspace_suspended | Workspace administratively suspended — trial-cliff expiry, chronic-abuse sweep, self-delete schedule, or manual operator action. Audit event per blocked request: WORKSPACE_SUSPENDED_BLOCK. 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. |
| 403 | session_killed | session_killed | From 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. |
| 413 | input_size_error | input_too_large | Request exceeds guardrail.max-input-tokens, guardrail.max-messages-per-request, or guardrail.max-message-length. |
| 413 | a2a_error | a2a_message_too_large | An 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. |
| 422 | schema_validation_error | schema_validation_failed | The response failed output-schema validation. DVARA does not retry this failure automatically. |
| 429 | rate_limit_error | rate_limit_exceeded | Per-key request count or token budget exhausted in the current 60-second window. Emitted directly by the rate-limit filter. |
| 429 | priority_throttle_error | priority_throttled | Priority admission controller rejected the request because the workspace's tier is over its concurrency threshold. |
| 429 | provider_rate_limited | provider_rate_limited | The upstream provider credential's published rate-limit budget is exhausted; the gateway shed the request after finding no capable non-shed fallback. Carries a Retry-After header derived from the upstream signal. Requires dvara.llm-gateway.provider-rate-limit.enabled=true. |
| 502 | provider_error | provider_response_too_large | From 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. |
| 502 | provider_error | provider_error | Upstream provider returned a non-success status, 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. |
| 503 | provider_unavailable | provider_circuit_open | The provider's circuit breaker is OPEN; no upstream call was attempted. |
| 503 | provider_unavailable | workspace_credential_unavailable | A 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. |
| 503 | provider_unavailable | failover_capability_mismatch | Primary 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.
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | prompt_template_not_active | Referenced template is not in ACTIVE status. |
| 400 | invalid_request_error | prompt_variable_missing | Required {{variable}} not provided in the request's variables map. |
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
| Status | type | code | When |
|---|---|---|---|
| 403 | guardrail_violation | hallucination_detected | The grounding-detection layer fires with action: BLOCK — response contains ungrounded claims. |
| 500 | guardrail_plugin_error | guardrail_plugin_error | An external guardrail plugin configured with fail-mode: CLOSED failed to complete its call. |
Flightdeck errors
Emitted by the DVARA Flightdeck on port 8090. Same envelope shape as the data plane, same lowercased-code convention.
These codes predate the removal of the Admin API in 1.7.0 and are now raised by the Flightdeck rather than by /v1/admin/**, which returns 404. The codes and their meanings are unchanged.
Authentication and authorization
| Status | type | code | When |
|---|---|---|---|
| 401 | unauthorized_error | authentication_required | Admin caller missing or invalid Bearer token / PAT / JWT. Emitted on authentication failure. |
| 403 | forbidden_error | access_denied | Workspace-scope violation: a workspace-role caller (admin / developer / viewer) tried to read or write a resource belonging to another workspace, or a caller lacked the URL-pattern RBAC role required for the path. Emits a WORKSPACE_SCOPE_VIOLATION audit event capturing the path, method, requested workspace id, and acting principal — security teams can detect probing without grepping response logs. |
Request validation and parsing
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | validation_error | Missing required field on the admin request DTO (e.g. name, email). Emitted when request validation fails. |
| 400 | invalid_request_error | invalid_json | Malformed JSON request body — parsing failed before DTO binding. |
Not-found codes (all 404 not_found_error)
Every admin resource that supports GET /{id} returns this shape when the ID doesn't exist or belongs to a different workspace than the caller's RBAC scope allows:
workspace_not_found · api_key_not_found · route_not_found · route_version_not_found · policy_not_found · policy_version_not_found · user_not_found · credential_not_found · pricing_not_found · budget_not_found · chargeback_not_found · report_not_found · webhook_not_found · mcp_server_not_found · schema_not_found · session_not_found · prompt_template_not_found · prompt_version_not_found · prompt_experiment_not_found · mock_scenario_not_found · golden_prompt_not_found · drift_report_not_found · eval_prompt_not_found · eval_report_not_found
Request-shape and build-availability codes
An *_not_available code means the optional implementation is not available in
this build. It does not describe the licence state. From 1.8.2 no plane needs a
licence, and an expired licence refuses no traffic. The only licence-related
refusal is the call limit for an install without a licence; see
Licence limits.
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request_error | invalid_request | Generic request-shape rejection. |
| 400 | invalid_request_error | invalid_policy_status | Policy status change target is not a valid lifecycle value. |
| 400 | invalid_request_error | invalid_template_status | Template status change target is not a valid lifecycle value. |
| 400 | invalid_request_error | invalid_report_type | Compliance report type is not one of SOC2 / HIPAA / GDPR. |
| 400 | invalid_request_error | compliance_not_available | Compliance reporting is not available in this build. The Enterprise Development posture does not licence-gate reporting when the implementation is present. |
| 400 | invalid_request_error | chargeback_not_available | Chargeback reporting is not available in this build. The Enterprise Development posture does not licence-gate reporting when the implementation is present. |
| 400 | invalid_request_error | mcp_not_available | The requested MCP operation is not available in this build. No licence is needed for the MCP Gateway. |
| 400 | invalid_request_error | encryption_not_configured | DVARA_ENCRYPTION_MASTER_PASSWORD is not set, so credential or guardrail-plugin secrets cannot be encrypted for storage. |
| 400 | invalid_request_error | prompt_experiment_not_running | Experiment is not in RUNNING status. |
| 400 | invalid_request_error | mock_scenario_invalid_name | Scenario filename failed path-traversal validation. |
| 400 | invalid_request_error | mock_scenario_invalid | Scenario Groovy source failed compile-check. |
| 400 | invalid_request_error | invalid_output_schema_scope | Registered output schema must declare a routeId, a modelPattern, or both — neither would never match any request. |
| 400 | invalid_request_error | invalid_policy_dsl | Policy YAML DSL failed compile-check (syntax error, unknown condition, unknown action). |
| 400 | invalid_request_error | invalid_storage_mode | Credential storage_mode is not one of ENCRYPTED / REFERENCE. |
| 400 | invalid_request_error | credential_api_key_missing | Creating or rotating a credential in the Flightdeck with storageMode=ENCRYPTED but no api_key field in the body. |
| 400 | invalid_request_error | credential_reference_missing | Creating or rotating a credential in the Flightdeck with storageMode=REFERENCE but no secret_reference field in the body. |
| 400 | invalid_request_error | credential_storage_mode_mismatch | Rotate request used the wrong field for the credential's current storage mode (e.g. passed api_key on a REFERENCE credential). |
| 400 | invalid_request_error | credential_not_rotatable | Tried to rotate a credential that isn't in ACTIVE status (e.g. already REVOKED or SUPERSEDED). |
| 409 | duplicate_error | mcp_server_duplicate | Server ID already exists for the target workspace. From 1.8.4 the Flightdeck forms show this refusal on the form, naming the ID, and keep what you typed. |
| 409 | duplicate_error | a2a_agent_duplicate | Agent ID already exists in the workspace, on create or on a rename. From 1.8.4 the Flightdeck refuse it on the form, naming the ID, and keep what you typed; before, the operator saw "an unexpected error occurred". |
| 409 | duplicate_error | user_duplicate | User email already exists for the target workspace. |
| 413 | payload_too_large | mock_scenario_too_large | Scenario source exceeded the 256 KB limit. |
| 500 | admin_error | — | Default fall-through for any unmatched gateway-thrown error. |
From 1.8.4 a new A2A agent ID may not contain ::, which DVARA uses to join an
agent to a skill. The Flightdeck, the Flightdeck and a GitOps import refuse it with
A2A_AGENT_ID_INVALID, naming the ID; use -, _ or . instead. An agent that
already has such an ID keeps it.
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 — comes back through the same lowercase-code, type: "mcp_error" envelope. MCP_WORKSPACE_SUSPENDED is the exception and is noted in the table below; a client that keys on type will not recognise it.
{
"error": {
"message": "…",
"type": "mcp_error",
"code": "mcp_server_not_found",
"trace_id": "…"
}
}
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.
| Status | code | When |
|---|---|---|
| 400 | mcp_not_available | The MCP implementation needed for this operation is not available in this build. Do not use this code to infer licence state. |
| 400 | mcp_upstream_rejected | From 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. |
| 410 | mcp_transport_sunset | From 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. |
| 413 | mcp_arguments_too_large | From 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. |
| 400 | mcp_request_state_invalid | From 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. |
| 400 | mcp_pii_detected | PII detected in the tool-call arguments or response, workspace policy is BLOCK. |
| 401 | mcp_auth_required | Missing Authorization: Bearer <key> on the MCP Gateway request. |
| 401 | mcp_auth_invalid | API key does not resolve to an active workspace. |
| 401 | mcp_auth_revoked | API key has been revoked. |
| 403 | mcp_policy_denied | MCP request denied by a policy rule. |
| 403 | mcp_approval_denied | A human reviewer denied the tool call from the DVARA Flightdeck approval queue. |
| 403 | mcp_approval_unavailable | From 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. |
| 403 | MCP_WORKSPACE_SUSPENDED | The 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. This one refusal does not use the envelope above — measured on 1.7.0, it returns {"error":{"message":"Workspace is suspended","code":"MCP_WORKSPACE_SUSPENDED"}}: the code is UPPERCASE, and there is no type and no trace_id. Match on the status and the code, not on type. |
| 403 | mcp_session_killed | The 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. |
| 403 | mcp_sampling_refused | From 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. |
| 403 | mcp_input_request_refused | From 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. |
| 404 | not_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. |
| 404 | mcp_server_not_found | MCP server ID not registered for the calling workspace. |
| 408 | mcp_approval_timeout | Approval gate reached its configured timeout with no human decision; workspace default action is deny. |
| 429 | mcp_rate_limit_exceeded | Per-key rate limit exhausted. |
| 429 | mcp_agent_loop_detected | The loop detector fired (repetition threshold, cycle detection, or rate cap) on the session. |
| 503 | mcp_injection_scan_failed | A tool response could not be scanned and the workspace injection action is BLOCK or SANITIZE; FLAG records the failure and returns the response. |
| 502 | mcp_response_too_large | From 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. |
| 502 | mcp_upstream_error | From 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. |
| 502 | mcp_server_unavailable | A 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.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>. |
| 503 | mcp_server_unavailable | The MCP server is registered but not active, for example suspended, disabled or quarantined; the message names the state. |
| 500 | mcp_internal_error | Any 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 dvara.llm-gateway.providers.<name>.api-key in application.yml or as an env var.",
"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"
}
}
Workspace Not Found
{
"error": {
"message": "Workspace not found: t-999",
"type": "not_found_error",
"code": "workspace_not_found",
"trace_id": "e9016783ab4c5d60c3ff042e3h3h7266"
}
}
Credential Not Found
{
"error": {
"message": "Required credential not found: provider.openai.api-key",
"type": "invalid_request_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:
| Status | Hint |
|---|---|
| 401 | invalid API key — check your provider credentials |
| 403 | access denied — your API key may lack required permissions |
| 404 | model or endpoint not found — check the model name |
| 429 | rate limited by provider — too many requests |
| 500 | provider internal server error |
| 502 | provider returned bad gateway |
| 503 | provider 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": "c8905672de3f5049b2dd931e2g2g6155"
}
}
PII Detected (Block Mode)
{
"error": {
"message": "PII detected in request — 2 entity type(s) found. Workspace policy: BLOCK",
"type": "pii_violation",
"code": "pii_detected",
"trace_id": "g0127894ab5c6d71e4ff153f4i4i8377"
}
}
Guardrail Blocked
{
"error": {
"message": "Request blocked: guardrail violation detected (INJECTION, JAILBREAK)",
"type": "guardrail_violation",
"code": "guardrail_blocked",
"trace_id": "h1238905bc6d7e82f5gg264g5j5j9488"
}
}
Input Too Large
{
"error": {
"message": "Request exceeds maximum messages limit: 150 > 100",
"type": "input_size_error",
"code": "input_too_large",
"trace_id": "i2349016cd7e8f93g6hh375h6k6k0599"
}
}
Schema Validation Failed
{
"error": {
"message": "Output schema validation failed: $.name: required property missing",
"type": "schema_validation_error",
"code": "schema_validation_failed",
"trace_id": "j3450127de8f9004h7ii486i7l7l1600"
}
}
Context Window Exceeded
{
"error": {
"message": "Estimated token count 150000 exceeds context window 128000 (no pruning strategy configured)",
"type": "context_window_error",
"code": "context_window_exceeded",
"trace_id": "k4561238ef9g0115i8jj597j8m8m2711"
}
}
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
Not included in DVARA Open Source.
| Status | Code | Meaning |
|---|---|---|
| 400 | a2a_not_available | The A2A implementation is unavailable in this build. |
| 400 | a2a_pii_detected | The configured PII action refused the message. |
| 401 | a2a_auth_required, a2a_auth_invalid, a2a_auth_revoked | The plane key is missing, invalid, or revoked. |
| 401 | a2a_delegated_token_required | A delegated agent call lacks its delegated token. |
| 402 | a2a_cap_exceeded | The 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. |
| 403 | a2a_policy_denied, a2a_scope_denied, a2a_workspace_suspended | Policy, scope, or workspace posture refused the call. |
| 403 | a2a_session_killed | The 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. |
| 403 | a2a_delegation_chain_required, a2a_delegation_chain_invalid | The 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. |
| 403 | a2a_approval_denied, a2a_approval_unavailable | The required human approval was denied or unavailable. |
| 404 | a2a_agent_not_found | The agent is absent or outside the caller's workspace. |
| 408 | a2a_approval_timeout | The approval window expired. |
| 409 | a2a_delegation_cycle, a2a_delegation_depth_exceeded | The delegation guard refused a hop that loops back or makes the chain too deep. Do not retry. |
| 409 | a2a_delegation_hop_budget_exceeded | From 1.8.5. The session has forwarded as many hops as a2a.delegation.max-hops-per-session allows. Do not retry. |
| 429 | a2a_loop_detected | Loop detection stopped the agent chain. |
| 429 | a2a_session_rate_limited | From 1.8.5. The session passed the hop rate (60 a minute). Unlike a loop, the session is not killed. |
| 502 | a2a_endpoint_not_https | From 1.8.5. The agent's endpoint is not https://. DVARA does not contact the peer. Move the agent to https:// or disable it. |
| 502 | a2a_response_too_large | From 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. |
| 502 | a2a_card_unavailable, a2a_card_signature_invalid | Agent 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. |
| 503 | a2a_agent_unavailable | The registered agent cannot serve traffic. |
| 503 | a2a_delegation_binding_unavailable | Since 1.8.4: the gateway could not read which agent the API key acts as, in a workspace that requires a chain from agents. A retry may pass. |
| 503 | a2a_delegation_settings_unavailable | From 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. |
| 503 | a2a_delegation_hop_budget_unavailable | From 1.8.5, under the hardened preset only. The session's hop count could not be read in time. A retry may pass. |
| 503 | a2a_push_not_available | From 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. |
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
| Status | Code | Distribution | Meaning |
|---|---|---|---|
| 400 | invalid_path | Both | The data-plane path is not valid. |
| 401 | api_key_required, invalid_api_key, api_key_revoked, api_key_expired | Both | The data-plane credential is missing or unusable. |
| 402 | license_token_cap_exceeded | Enterprise | The monthly call limit is used up on /v1/*. See Licence limits. |
| 402 | mcp_cap_exceeded, a2a_cap_exceeded | Enterprise | The monthly call limit is used up on /mcp/* or /a2a/*. See Licence limits. |
| 403 | api_key_scope | Both | A valid key lacks the scope for this operation. |
| 403 | grounding_unavailable | Enterprise | Grounding was required but its evidence or engine was unavailable. |
| 403 | mcp_scope_denied, a2a_scope_denied | Enterprise | A plane key lacks the required scope. |
| 404 | batch_not_found | Both | The batch is absent from this workspace. |
| 404 | file_not_found | Both | The file is absent or belongs to another workspace; both cases deliberately look the same. |
| 404 | mcp_virtual_server_not_found | Enterprise | The virtual MCP server does not exist. |
| 409 | mcp_virtual_server_duplicate | Enterprise | The virtual MCP server identifier already exists. |
| 503 | workspace_credential_unavailable | Enterprise | A stored workspace credential exists but cannot be used; DVARA does not fall back. |
| 403 | egress_url_blocked | Enterprise | An outbound URL failed the egress or SSRF policy. |
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
400 no_provider | API key not set for the provider | Set the environment variable (e.g., OPENAI_API_KEY) |
403 data_residency_violation | Data residency policy violation | Configure routing to use providers in allowed regions |
400 unsupported_response_format | Provider doesn't support the format | Use a different provider or remove response_format |
400 no_capable_provider | No provider on the route supports it | Add a capable provider to the route |
404 workspace_not_found | Workspace ID does not exist | Verify the workspace ID via the Flightdeck |
404 route_not_found | Route ID does not exist | Verify the route ID via the Flightdeck |
404 route_version_not_found | Route version does not exist | Check the route's version history in Flightdeck |
404 report_not_found | Report ID does not exist | Verify the report ID via the Flightdeck |
400 pii_detected | PII found and action is BLOCK | Remove the value, use irreversible REDACT, or use LOG while calibrating |
403 pii_tokenize_unavailable | This build has no reversible token store | Use REDACT, or run the Enterprise tokenization prerequisites |
403 pii_redact_unavailable | Enterprise TOKENIZE cannot mint a token | Restore its token store or key path; inspect attempted_action, then retry |
403 guardrail_blocked | Injection/jailbreak/content detected | Review prompt content; change workspace guardrail action to FLAG or LOG |
413 input_too_large | Request exceeds input size limits | Reduce message count, message length, or total input tokens; increase workspace limits via metadata |
422 schema_validation_failed | Response doesn't match JSON schema | Review the registered schema via the Flightdeck and tighten or relax it; the registry does not yet retry on a single failure (maxRetries is reserved for a future correction-prompt flow). |
400 context_window_exceeded | Context window full, no pruning | Reduce conversation length; configure pruning strategy via workspace metadata (guardrail.context.pruning-strategy) |
402 budget_cap_hard | Hard budget limit exceeded | Wait for the next budget period or raise the cap in Flightdeck |
400 compliance_not_available | Compliance reporting is not available in this build | Run an Enterprise build that includes reporting |
400 invalid_report_type | Invalid report type specified | Use one of: SOC2, HIPAA, GDPR |
400 mcp_not_available | The requested MCP implementation is not available in this build | Run a build that includes that MCP capability |
401 mcp_auth_required | Missing Bearer token on MCP Gateway | Include Authorization: Bearer gw_<key> header |
401 mcp_auth_invalid | Invalid API key on MCP Gateway | Verify the API key is correct |
401 mcp_auth_revoked | API key revoked on MCP Gateway | Generate a new API key |
404 mcp_server_not_found | MCP server ID does not exist | Verify server ID via the Flightdeck |
409 mcp_server_duplicate | Server ID already exists for workspace | Use a different server_id or check existing servers |
403 mcp_policy_denied | MCP request denied by policy | Check policy rules via the Flightdeck. Review mcp_server, mcp_tool, mcp_arg conditions. |
400 mcp_upstream_rejected | The MCP server refused the call, e.g. unknown tool or bad arguments | Read error.upstream_error.message; fix the tool name or arguments. Do not retry unchanged. |
502 mcp_upstream_error | The MCP server answered with a JSON-RPC error | Read error.upstream_error and the server's own logs for the trace_id |
502 mcp_server_unavailable | MCP server unreachable, timed out, or its session did not start | Check the server's address and health; the call can be retried |
403 mcp_approval_unavailable | Approval store unreachable, default action deny | Restore Flightdeck and its approval store; retrying before then fails the same way |
503 mcp_server_unavailable | MCP server suspended/disabled | Re-activate the server or check its upstream health in Flightdeck |
429 rate_limit_exceeded | Too many requests | Wait for Retry-After seconds, or increase limits |
502 provider_error | Upstream provider returned an error | Read the hint in the error message (e.g., "invalid API key", "rate limited"). For 401: check your provider API key env var. For 429: reduce request rate. For 5xx: check the provider's status page. The gateway retries automatically. |
503 provider_circuit_open | Too many recent provider failures | The 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_mismatch | Primary failed, no capable fallback | Add more providers to the route that support the capability |