Errors and limits

The public sales agent reports errors in one of two ways, depending on what you called.

How errors arrive

Tool calls (tools/call) return errors as an AdCP error inside a normal MCP tool result, with HTTP 200:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "isError": true,
    "structuredContent": {
      "adcp_error": {
        "code": "UNSUPPORTED_FEATURE",
        "message": "create_media_buy is not supported by this agent",
        "recovery": "correctable"
      }
    }
  }
}

Read result.structuredContent.adcp_error. It always has code, message and recovery, and it can also have field (the request field at fault, as a JSON Pointer for VALIDATION_ERROR), issues, retry_after (seconds, on RATE_LIMITED) or details.

Protocol methods (initialize, tools/list, and other non-tool MCP methods) have no tool result, so errors come back as a JSON-RPC error with a matching HTTP status. The AdCP code is in error.data.code:

{
  "jsonrpc": "2.0",
  "id": 7,
  "error": {
    "code": -32001,
    "message": "Invalid or revoked bearer token",
    "data": { "code": "AUTH_REQUIRED" }
  }
}

Error codes

CodeRecoveryWhen you get it
AUTH_REQUIREDcorrectableYou sent an Authorization header, but the key is invalid or has been revoked. The agent doesn’t fall back to anonymous access. On a protocol method this is HTTP 401 with WWW-Authenticate: Bearer.
RATE_LIMITEDtransientYou went over your per-minute limit (see below). retry_after and the Retry-After header give the seconds until your window resets. On a protocol method this is HTTP 429.
UNSUPPORTED_FEATUREcorrectableYou called a tool the agent doesn’t expose. Only get_adcp_capabilities, list_creative_formats and get_products are supported (see supported tools).
VALIDATION_ERRORcorrectableThe request doesn’t match the AdCP 3.1 request schema, e.g. an unknown buying_mode or a lower-case country code. field is a JSON Pointer to the first bad value (/filters/countries/0); issues lists every one.
INVALID_REQUESTcorrectableThe request broke a rule the agent enforces: buying_mode: "brief" with an empty brief (field: "brief"), or more than 100 format_ids in list_creative_formats.
VERSION_UNSUPPORTEDcorrectableThe request asked for an AdCP version the agent doesn’t serve, e.g. adcp_major_version: 2. details.supported_versions lists what it does serve (["3"]).
SERVICE_UNAVAILABLEtransientThe agent couldn’t complete the request right now. On a protocol method, or when the agent is paused, this is a JSON-RPC error with HTTP 503 (or 500 for an unexpected failure).

What each recovery value means

  • transient: nothing is wrong with your request. Retry automatically with exponential backoff. For RATE_LIMITED, wait the retry_after seconds first.
  • correctable: fix the request before sending it again. Resending the same request gets the same error. For AUTH_REQUIRED a person usually needs to step in, because the key needs replacing.

Transport-level errors

A few errors come from the MCP transport before any tool runs. They have no AdCP code:

HTTPWhat happened
400The body was a JSON-RPC batch (a JSON array). Send one request per POST.
405You used GET or DELETE. The agent only accepts POST.
406Your Accept header didn’t include both application/json and text/event-stream.

Request validation

Every tool call is checked against the AdCP 3.1 request schema before the agent does any work. A request that breaks it gets VALIDATION_ERROR with the failing field, for example:

{
  "adcp_error": {
    "code": "VALIDATION_ERROR",
    "message": "get_products request failed schema validation at /filters/countries/0: must match pattern \"^[A-Z]{2}$\"",
    "recovery": "correctable",
    "field": "/filters/countries/0",
    "issues": [
      {
        "pointer": "/filters/countries/0",
        "message": "must match pattern \"^[A-Z]{2}$\"",
        "keyword": "pattern"
      }
    ]
  }
}

The same issues list is also in details.issues. For an enum field such as buying_mode, each issue also has allowedValues.

Fix the field and send the request again. INVALID_REQUEST is for requests that pass the schema but break one of the agent’s own rules (see the table above).

Rate limits

CallerLimit
Anonymous30 requests per minute, per IP
With a key300 requests per minute, per key
  • Every request counts, including tools/list and initialize. MCP notifications don’t count.
  • Windows are fixed calendar minutes, so the count resets when the next minute starts.
  • Requests with an invalid key count against the anonymous limit for your IP.
  • When you’re over the limit, tool calls return RATE_LIMITED (HTTP 200, like every tool error) and protocol methods return HTTP 429. Both carry a Retry-After header with the whole seconds until your window resets (1 to 60), and the same number is in the error body: adcp_error.retry_after for tool calls, error.data.retry_after for protocol methods. Wait that long before retrying.

Safe to retry?

  • Retry automatically: RATE_LIMITED (after retry_after seconds) and SERVICE_UNAVAILABLE (with backoff). Every tool the agent exposes is read-only, so a retry can’t create anything twice.
  • Fix, then retry: VALIDATION_ERROR, INVALID_REQUEST, VERSION_UNSUPPORTED, UNSUPPORTED_FEATURE, and the transport errors above.
  • Needs a person: AUTH_REQUIRED means your key needs replacing, and a SERVICE_UNAVAILABLE that keeps coming back after several minutes of backoff means you should get in touch with the Carbnn team.