When a compliance storyboard fails against your agent, the runner reports a step name and error text. This page maps the most common error patterns to their root causes and fixes, so you can resolve each class of failure without spelunking through SDK source or runner internals. Each section shows the error you’ll see, what it means, and what to change in your agent.Documentation Index
Fetch the complete documentation index at: https://agenticadvertisingorg-changeset-release-main.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Unknown fixture errors
sample_request references a hardcoded ID (test-product, test-pricing, campaign_hero_video, gov_acme_q2_2027, etc.). The runner expects the agent to have that ID in its catalog before the mutating step runs.
Fix: Implement comply_test_controller and honor the seed scenarios declared in the storyboard’s fixtures: block. When prerequisites.controller_seeding: true is set, the runner auto-injects a fixtures phase that calls seed_product, seed_pricing_option, seed_creative, seed_plan, or seed_media_buy in foreign-key order before the main phases execute.
See Compliance test controller — Scenarios for the full seed contract. Agents that return UNKNOWN_SCENARIO on a seed call grade the storyboard not_applicable — they are not penalized for missing sandbox surface, but they cannot pass storyboards that depend on pre-seeded state.
Signature challenge missing on 401
get_adcp_capabilities.request_signing.required_for. Your agent rejected with 401 but did not include a WWW-Authenticate: Signature ... challenge header, so the runner could not resolve the error code from the transport binding.
Fix: Emit the RFC 9421 challenge header on every 401 caused by missing or invalid signatures. The runner resolves the error code via the transport binding order — if the WWW-Authenticate header is absent, the error classification falls back to “(none)” even when the JSON body carries a useful message.
The reference SDK constructs these errors via RequestSignatureError from @adcp/sdk/signing with .code: RequestSignatureErrorCode. The full taxonomy (request_signature_required, request_signature_header_malformed, request_signature_tag_invalid, request_signature_window_invalid, request_signature_key_unknown, etc.) is enumerated in that module. Your agent SHOULD surface the same code on the challenge so SDK-speaking callers can recover automatically.
See Signed Requests (Transport Layer) for the challenge-header format and the transport-error binding order.
Response envelope drift
check: error_code but your response surfaces the error on a shape the runner’s client-detection order didn’t expect. In practice, this means your agent returned errors[] when the transport layer already carried adcp_error, or vice versa — the storyboard asserted a single error code and the runner resolved it from a different layer than you emitted on.
Fix: Pick one error surface per response and stick to it per the envelope vs. payload two-layer model. On MCP: adcp_error for structured content; errors[] for task-payload errors. On A2A: the same layers apply — transport error in the envelope, application error in the task artifact’s DataPart.
The runner’s check: error_code is shape-agnostic — it resolves from either layer — but if your agent emits both simultaneously they can disagree, and the runner grades the resolved code against the vector’s expectation. Choosing one surface and being consistent avoids the divergence.
Context echo failures
context: object from the request. Every storyboard step that sends context: { correlation_id: ... } asserts that context.correlation_id echoes unchanged in the response.
Fix: Preserve the full context: object verbatim on every response, including errors. The echo contract is normative — buyers use correlation_id to stitch multi-agent flows, and the runner grades every context-carrying step on it. See Context and sessions — Normative echo contract.
Captures use the same contract in reverse: storyboards that pass "$context.<name>" through context_outputs: rely on the capture populating after the producer step’s validations pass. A downstream step reading $context.foo when the producer failed or omitted context: grades as unresolved_substitution.
Capability-vector mismatch (runner declared, agent doesn’t support)
get_adcp_capabilities response. The runner should auto-skip these steps; if you’re seeing them graded as failures instead, either the capability is declared at the wrong key or the runner is missing the auto-skip path.
Fix: Double-check your get_adcp_capabilities.tools list and any required-for fields (request_signing.required_for, idempotency.supported_tools, etc.). For vectors that apply only to specialized agents, the storyboard author can use skipVectors to flag the opt-out explicitly; as an implementer, the fix is almost always on the capability declaration rather than the vector.
Required-for composition
auth.api_key AND the agent doesn’t advertise request-signing support — leaving the runner with no way to authenticate the call.
Fix: Either (a) declare auth.api_key in the test kit so the runner uses Bearer auth, or (b) advertise request-signing via get_adcp_capabilities.request_signing so the runner signs the request instead. The runner’s requireAuthenticatedOrSigned gate accepts either path — it fails only when both are absent.
Bearer-only agent: no auth mechanism contributed (assert_mechanism)
mechanism_required phase found no contribution to auth_mechanism_verified from either prior phase. This is the most common failure for Bearer-only (API-key-only) agents, and it is not a real auth problem — it is a test-kit configuration gap.
What happened: The api_key_path phase has skip_if: "!test_kit.auth.api_key" — it is skipped entirely unless the test kit declares an API key. The oauth_discovery phase 404s on /.well-known/oauth-protected-resource/... (expected for Bearer-only agents; those failures are silently ignored by the runner). With both phases contributing nothing, assert_mechanism sees actual: [].
The --auth TOKEN distinction: The --auth TOKEN flag you pass to the runner is the runner’s own session credential — it authorizes the runner’s own requests to your agent. It is entirely separate from test_kit.auth.api_key, which is the specific Bearer value the api_key_path phase sends during its positive-key and invalid-key probes. These are not the same token and are not interchangeable.
Fix: Every AdCP test kit declares its probe API key under auth.api_key using the demo-<kit>-v1 naming convention. The default test kit (acme-outdoor) uses demo-acme-outdoor-v1. Configure your agent to accept the kit’s probe key as a valid compliance-testing credential alongside your production key. The demo-<kit>- prefix is the AdCP conformance handle — accept any Bearer token matching the prefix for the kits you run against (the suffix can rotate across spec versions, the prefix stays stable):
api_key_path (no test-kit key matches), fail oauth_discovery (no PRM), and land at actual: [] in assert_mechanism. Adding the demo key to your allowed-keys set is sufficient — you do not need a PRM endpoint or an OAuth issuer.
Do not serve a fake /.well-known/oauth-protected-resource/... pointing at a non-existent issuer to “pass” the OAuth phase. That triggers the advertised-but-unserved failure mode the storyboard was designed to catch.
See Known spec ambiguities — PRM required for non-OAuth agents for background on why the carve-out exists and how the api_key_path / oauth_discovery phase semantics were designed.
INVALID_STATE vs INVALID_TRANSITION
Two codes that are easy to confuse:
INVALID_STATE— the canonical AdCP media-buy error code for “the resource is in a state that doesn’t allow this action.” Used oncreate_media_buy/update_media_buy/pause/resume/cancelagainst a media buy that cannot transition as requested. Seemedia-buy/specification.mdxandmedia-buy/media-buys/index.mdxfor authoritative usage.INVALID_TRANSITION— specific to thecomply_test_controllersandbox primitive. Emitted when a runner requests a state-machine transition the seller rejects (e.g., forcingapproved→archivedwithout going throughactive). See Compliance test controller — Scenarios.
INVALID_STATE on a production task but your agent returns INVALID_TRANSITION is an error-code vocabulary mismatch — INVALID_TRANSITION is not in the canonical enum at static/schemas/source/enums/error-code.json and should not appear outside the compliance test controller.