error.json schema maps to MCP and A2A response envelopes.
For the error schema itself, standard codes, and recovery strategies, see Error Handling.
Layer Separation
Transport errors are handled by protocol libraries. Application errors are handled by business logic. Mixing them loses the structured recovery data that makes AdCP errors useful.
MCP Binding
Tool-Level Errors
The standard path for all AdCP error codes. The tool executed, understood the request, and is returning a structured error. Today’s practical path: Most MCP hosts (Claude Desktop, Cursor, Windsurf) readcontent text on error responses and do not surface structuredContent to LLMs or programmatic consumers. Until structuredContent adoption is widespread, the text-fallback path is how most errors will be extracted. Servers SHOULD support both paths:
content text carries the AdCP error as a JSON string for text-based extraction. structuredContent.adcp_error carries the same error for programmatic clients that support it. Servers that include human-readable text SHOULD add it as a second content item, keeping it terse (one sentence):
structuredContent is present. When structuredContent carries the full error, the human-readable text content item SHOULD be a single terse sentence (e.g., “Rate limited — retry in 5s.”). The error details are already in structuredContent and the JSON text fallback. Repeating the full error in prose wastes LLM context tokens — especially for transient errors that accumulate during retries.
adcp_error key: Namespacing avoids collisions with success data that may also appear in structuredContent (e.g., products). A single key simplifies detection.
structuredContent requires MCP 2025-03-26 or later. Servers on older MCP versions omit structuredContent — the JSON string in content[0].text is sufficient. Clients parse this via the text-fallback path (see Client Detection Order).
Transport-Level Errors
When infrastructure rejects a request before tool dispatch (API gateway, rate-limit middleware), the tool never executes. Use a reserved JSON-RPC error code with the AdCP error indata:
Reserved JSON-RPC Codes
These codes are in the JSON-RPC server-defined range (
-32000 to -32099). All other AdCP error codes use the tool-level path exclusively.
MCP server SDK note: Throwing
McpError from inside a tool handler produces a JSON-RPC error response — the SDK does not convert it to an isError: true tool result. This means -32029 works the same way whether thrown from middleware or a tool handler. However, application-layer errors (where the tool understood the request and is returning a structured failure) should use the isError: true tool-level path above, not JSON-RPC error codes. Reserve -32029/-32028/-32027 for infrastructure that rejects requests before tool dispatch.MCP Server Implementation
A2A Binding
Failed Tasks
Usestatus: "failed" with the AdCP error in an artifact DataPart, plus a TextPart for human/LLM consumption:
.artifacts for data.
Relationship to the “no wrappers” rule. The adcp_error key is an intentional exception for failed tasks. Unlike success responses where DataPart contains task-specific data (e.g., products), a failed task’s DataPart contains only the error. The key acts as a type discriminator so clients can distinguish error from success payloads without relying solely on status.
Error MIME Type (Optional)
A2A agents MAY setmetadata.mimeType on the error DataPart:
adcp_error key is the authoritative signal.
Envelope vs. payload errors
AdCP exposes errors in two distinct places. This page covers the transport envelope (adcp_error); the payload errors array (errors[]) is covered in Error Handling — Envelope vs. payload errors.
A fatal task failure SHOULD populate both layers — see the canonical
protocol-envelope.json examples and the error-handling.mdx reference for the normative SHOULD.
Client Detection Order
Clients MUST check for AdCP errors in this order:structuredContent.adcp_error(withisError: true) — MCP tool-level errorartifacts[].parts[].data.adcp_error— A2A task-level error (artifacts)status.message.parts[].data.adcp_error— A2A task-level error (status message)error.data.adcp_error— JSON-RPC transport-level error- JSON-parsed
content[].textwithadcp_errorkey — Text fallback for older MCP servers (only forisErrorresponses) payload.errors[0](or top-levelerrors[0]) — payload-layer fallback. Used when the transport envelope does not surfaceadcp_errorbut the payload carries anerrors[]array. Reading from the payload is legitimate for non-fatal cases where only the payload layer is populated (e.g.,input-requiredtasks reporting warnings), but a fatal task that surfaces errors only via the payload is a conformance gap on the agent side.- No structured error found — fall back to generic error handling.
code field of type string. If validation fails, treat as no structured error found.
Storyboard check: error_code contract
Storyboard validators use check: error_code rather than path-specific assertions because the error may surface on either layer. The runner contract:
check: error_coderesolves the error code by running the client detection order above — preference in order:adcp_error.code(transport) →errors[0].code(payload).- If neither layer carries a
code, the validation fails witherror_code_not_resolvable. - Storyboard authors SHOULD NOT pin assertions to a specific path (e.g.,
check: field_present, path: "errors") — that couples the test to one layer and fails against agents that surface errors on the other. See Storyboard authoring — Asserting on errors.
adcp_error object with field values preserved as-is (including out-of-range retry_after). Clamping, retry logic, and other behavioral requirements apply at the action layer (see Recovery Behavior).
In practice, implementations branch on transport type first and only check the relevant paths:
Recovery Behavior
Once extracted, apply recovery based on therecovery field:
retry_after bounds: Sellers MUST return retry_after values between 1 and 3600 seconds. Clients MUST clamp values outside this range: values below 1 become 1, values above 3600 become 3600. Non-finite values (NaN, Infinity) MUST be treated as absent. This prevents both aggressive retry loops and pathologically long stalls from misconfigured servers.
Retry ceiling: Buyer agents SHOULD enforce a maximum retry count (e.g., 3 attempts) and a maximum cumulative retry duration (e.g., 300 seconds) per operation. Transient errors that persist beyond the retry budget SHOULD be escalated as terminal. Without a ceiling, a malicious or misconfigured seller returning retry_after: 3600 on every request can stall an agent indefinitely.
When recovery is absent: Fall back to code-based classification using the standard error code table. This allows Level 1 servers (which return code + message only) to still get correct recovery behavior from capable clients. If the code is also unknown, treat as terminal.
For unknown recovery values (forward compatibility), treat as terminal.
Recommended details Shapes
The details field is an open object. To prevent interoperability divergence, sellers SHOULD use these standard keys when populating details for common error codes:
RATE_LIMITED
BUDGET_TOO_LOW
AUDIENCE_TOO_SMALL
ACCOUNT_SETUP_REQUIRED
CREATIVE_REJECTED
POLICY_VIOLATION
CONFLICT
Size Guidance
Sellers SHOULD keepdetails compact. Error responses flow through LLM context windows where every token has a cost — and transient errors that trigger retries can accumulate multiple error responses in a single conversation. As a guideline, keep details under 500 serialized JSON bytes (use JSON.stringify(details).length in UTF-8 — this matters for non-ASCII content).
details Schemas
JSON Schemas for all recommended details shapes are published alongside the error code enum:
/schemas/v3/error-details/rate-limited.json/schemas/v3/error-details/budget-too-low.json/schemas/v3/error-details/audience-too-small.json/schemas/v3/error-details/account-setup-required.json/schemas/v3/error-details/creative-rejected.json/schemas/v3/error-details/policy-violation.json/schemas/v3/error-details/conflict.json
details entirely are conformant. Agents MUST NOT require specific details keys — fall back to code, message, and recovery when details is absent or has unexpected shape.
Seller-Specific Error Codes
Sellers MAY use error codes not in the standard vocabulary. To distinguish seller-specific codes from standard codes and avoid collisions between sellers:- Seller-specific codes MUST use the format
X_{VENDOR}_{CODE}(e.g.,X_STREAMHAUS_FLOOR_NOT_MET) {VENDOR}MUST be an uppercase alphanumeric identifier (matching/^[A-Z][A-Z0-9]{1,19}$/) registered in the vendor error code registry{CODE}MUST be uppercase alphanumeric with underscores (matching/^[A-Z][A-Z0-9_]{1,39}$/)- Agents MUST handle unknown codes by falling back to the
recoveryclassification - If
recoveryis absent on an unknown code, treat asterminal - Sellers SHOULD register their vendor prefix and codes in the vendor error code registry by submitting a PR
Client Library Requirements
Client libraries (like@adcp/sdk) that implement this spec MUST:
-
Extract structured errors automatically. Consumers should receive a typed error object with
code,recovery,retryAfter,field,suggestion, anddetails— not a generic error with a message string. -
Implement the detection order. Check all paths in order:
structuredContent, artifacts,status.message.parts,error.data, text fallback. -
Validate extracted errors. Verify that
codeis a non-empty string (max 64 characters) and that the total serialized payload does not exceed 4096 bytes. Discard payloads that fail validation. -
Guard text fallback with
isError. Only attempt JSON-based text extraction on MCP responses whereisErroristrue. A successful response with JSON content MUST NOT be interpreted as an error. -
Preserve recovery metadata. The extracted error MUST carry
recoveryandretry_afterso callers can implement retry logic without re-parsing. -
Handle unknown recovery values. Treat unknown
recoveryvalues asterminal. -
Clamp
retry_after. Values below 1 become 1, values above 3600 become 3600. Non-finite values (NaN,Infinity) MUST be treated as absent. -
Support text fallback. Attempt
JSON.parseoncontent[].textfor MCPisErrorresponses withoutstructuredContent. This will be the primary extraction path untilstructuredContentadoption is widespread.
- Auto-retry
transienterrors with exponential backoff whenretry_afteris present - Expose a
retryPolicyoption for consumers to configure retry behavior - Map standard error codes to typed error subclasses using the
STANDARD_ERROR_CODEStable
Test Vectors
Machine-readable test vectors are available at/static/test-vectors/transport-error-mapping.json. Each vector contains:
transport:mcpora2apath: extraction path (structuredContent,jsonrpc_error,text_fallback,artifact)response: the transport-specific response envelopeexpected_error: the AdCP error that should be extracted (ornullfor legacy servers)expected_action:retry,surface_to_caller,escalate_to_human, orgeneric_error
Error Translation in Agent Chains
When a seller agent calls upstream services (APIs, databases, other agents), upstream failures must be translated before returning to the caller. Rule 1: Translate upstream errors into AdCP error codes. Do not pass through raw upstream errors. An HTTP 429 from a seller’s internal API becomesRATE_LIMITED. A database connection timeout becomes SERVICE_UNAVAILABLE. The buyer should never see error formats from systems it has no relationship with.
Rule 2: Classify recovery from the caller’s perspective. If the seller can fix the upstream issue without buyer action, the error is transient or terminal — not correctable. A correctable error means the buyer needs to change something. For example: if the seller’s upstream creative review API rejects an ad, that is correctable (the buyer can revise the creative). But if the seller’s internal billing system is down, that is transient (the buyer should retry) even though the upstream error might be a 500.
Rule 3: Intermediaries preserve or translate, never drop. An orchestrator sitting between buyer and seller (e.g., an agency agent routing to multiple sellers) MUST either:
- Pass through the AdCP error unchanged if the upstream is already AdCP-conformant, or
- Translate the error into a valid AdCP error if the upstream uses a different format
recovery, retry_after, or details from errors they pass through. An intermediary MAY aggregate errors from multiple upstream sellers into an errors array, with each error preserving its original code and recovery.
Security Considerations
Error responses flow through LLM context. Every field is client-facing.Seller Requirements
Implementations MUST NOT include:- Internal service names, hostnames, or IP addresses
- Database error text, SQL fragments, or query plans
- Stack traces or file paths
- Upstream API responses from internal services
- Credentials, tokens, or session identifiers
suggestion boundaries: Provide generic correction guidance (e.g., “Increase budget to meet minimum”) rather than revealing specific thresholds, valid identifiers, or resource existence.
retry_after consistency: Return consistent values reflecting the caller’s rate-limit state, not the target resource’s properties, to avoid timing side channels.
Transport-level code granularity: The reserved JSON-RPC codes (-32029, -32028, -32027) enable infrastructure error classification. Implementations that prefer to minimize endpoint fingerprinting MAY collapse these into a single code.
Buyer Agent Requirements
Prompt injection via error fields. Themessage, suggestion, field, details, and all string values within them are seller-controlled content that enters buyer agent LLM context. A malicious or compromised seller can craft values containing instructions aimed at manipulating the buyer agent.
Buyer agents MUST:
- Route all recovery decisions through
codeandrecoveryonly. Never parsemessage,suggestion, ordetailsvalues for actionable instructions. ThehandleAdcpErrorfunction above demonstrates this pattern — it switches onrecovery, not on message content. - Use data boundaries for seller-provided strings. When including error field values in LLM context, place them inside explicit data delimiters (e.g., structured tool response fields, XML-style tags) that the system prompt designates as untrusted seller data. Do not interpolate seller-provided strings into prose or instructions.
- Enforce length limits before including seller strings in LLM context:
message(256 bytes),suggestion(512 bytes). Truncate silently. - Strip non-printable characters from all string fields: control characters (U+0000–U+001F), zero-width characters (U+200B–U+200F), and bidirectional override characters (U+202A–U+202E).
- Enforce a maximum payload size. Clients MUST discard extracted
adcp_errorobjects whereJSON.stringify(error).lengthexceeds 4096 bytes. This prevents context window exhaustion from oversizeddetailsobjects. - Never use
fieldas a dynamic property path in object mutation operations (e.g.,lodash.set, bracket notation chains). Thefieldvalue is for display and field-level UI highlighting only. - Never merge extracted error objects into application state via
Object.assign, spread operators, or shallow copy without filtering keys. Seller-controlled keys like__proto__orconstructorcan trigger prototype pollution in some runtimes. - Never include raw
detailsobjects in system prompts or tool descriptions.
details.setup_url (in ACCOUNT_SETUP_REQUIRED errors) is a seller-provided URL that users or agents may follow to complete account setup. Clients MUST validate that setup_url uses the https scheme, contains no userinfo component (e.g., https://user:pass@evil.com), and that the domain matches the seller’s known domain. Clients MUST reject URLs that fail any of these checks.
details.policy_url (in CREATIVE_REJECTED and POLICY_VIOLATION errors) is informational. Clients SHOULD apply the same validation. All seller-provided URLs MUST be rejected if they use non-https schemes (http, javascript, data, file).
See Also
- Error Handling — error schema, standard codes, recovery strategies
- MCP Guide — MCP transport integration
- A2A Guide — A2A transport integration
- A2A Response Format — canonical A2A response structure