<!-- Canonical HTML: https://autoagentprotocol.org/docs/v1.3/errors -->
<!-- Immutable contract: v1.3. -->
# Errors

![A JSON-RPC protocol error paired with a typed AAP domain error and validation details](https://autoagentprotocol.org/docs/v1.3/img/error-anatomy.svg)

AAP defines a single typed error payload (`aap.error`) that every dealer agent MUST use when a skill cannot be fulfilled. The error rides inside the standard A2A error envelope: `error.data` of the JSON-RPC error response ([Section 9.5](https://autoagentprotocol.org/docs/v1.3/bindings/json-rpc.md#error-mapping-a2a-section-95)). AAP v1.3.0 uses a single transport — JSON-RPC 2.0; the HTTP+JSON (REST) binding was [removed in v1.1.0](https://autoagentprotocol.org/docs/v1.3/bindings/rest.md).

## Error payload shape

```json
{
  "type": "aap.error",
  "error_id": "err_01HZ9EXAMPLE",
  "code": "SCHEMA_VALIDATION_FAILED",
  "message": "request failed validation with 2 errors",
  "retryable": false,
  "details": {
    "errors": [
      { "instanceLocation": "/filters/year_min", "keyword": "type", "error": "must be an integer" },
      { "instanceLocation": "/filters/make", "keyword": "additionalProperties", "error": "unknown filter key" }
    ]
  },
  "created_at": "2026-04-30T10:15:30Z"
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `type` | const | yes | Always `aap.error`. |
| `error_id` | string | yes | Unique identifier for this error instance (UUID recommended), suitable for support correlation. |
| `code` | enum | yes | One of the 12 AAP error codes below. |
| `message` | string | yes | Human-readable summary. May be surfaced to the end user. |
| `retryable` | boolean | yes | Whether the buyer agent SHOULD retry the same request after a backoff. |
| `details` | object | no | Code-specific details. For validation errors it carries an **`errors[]`** array listing EVERY problem at once (see below); other codes use it for retry hints, ids, etc. |
| `created_at` | date-time | yes | When the error was generated by the dealer agent. |

### `details.errors[]` — all problems at once

Validation errors (`SCHEMA_VALIDATION_FAILED`, `MISSING_REQUIRED_FIELD`, `INVALID_CONDITION`) put **every** failing field in a single `details.errors` array so a buyer agent fixes the whole payload in one pass instead of one round-trip per error. Each entry follows the JSON-Schema-2020-12 standard output unit shape:

| Field | Type | Description |
|---|---|---|
| `instanceLocation` | string | JSON Pointer to the failing field (e.g. `/consent/scope/0`). |
| `keyword` | string | The violated constraint (`type`, `required`, `additionalProperties`, `enum`, …). |
| `error` | string | Human-readable message for that field. |

A dealer agent MUST return all currently-detectable validation errors in one response — never just the first.

## Error code reference

The 12 codes, their meaning, recommended JSON-RPC code, and `retryable` default.

| `code` | Meaning | JSON-RPC | `retryable` default |
|---|---|---|---|
| `UNSUPPORTED_SKILL` | The agent does not implement this skill. | -32601 (Method not found) | `false` |
| `SCHEMA_VALIDATION_FAILED` | Request body fails JSON Schema validation. | -32602 (Invalid params) | `false` |
| `MISSING_REQUIRED_FIELD` | A specifically required field is absent. | -32602 (Invalid params) | `false` |
| `INVALID_CONDITION` | `vehicle_of_interest.condition` is in the trade-in vocabulary, or `trade_in.condition` is in the sale-condition vocabulary. | -32602 (Invalid params) | `false` |
| `VEHICLE_NOT_FOUND` | The supplied `vin` / `stock` / `vehicle_id` does not match any listing. | -32000 (Server error) | `false` |
| `VEHICLE_UNAVAILABLE` | The vehicle exists but its `status` is no longer one of `available` \| `intransit` \| `pending`. | -32000 (Server error) | `false` |
| `CONTACT_CONSENT_REQUIRED` | `customer` info present without `consent`, or follow-up channel not in `consent.allowed_channels`. | -32000 (Server error) | `false` |
| `INVALID_CONSENT` | `consent` is present but malformed, expired, or its scope does not cover the called skill. | -32000 (Server error) | `false` |
| `APPOINTMENT_TIME_UNAVAILABLE` | The requested `appointment_at` cannot be honored AND the dealer has no proposed alternatives. | -32000 (Server error) | `false` |
| `IDEMPOTENCY_CONFLICT` | An `idempotency_key` was reused with a different request payload. | -32000 (Server error) | `false` |
| `RATE_LIMITED` | Client has exceeded the dealer's rate limit. | -32002 (Server error reserved) | **`true`** |
| `INTERNAL_ERROR` | Unhandled dealer-side error. | -32603 (Internal error) | `true` |

`retryable` is a default, not a hard rule. Dealer agents MAY override it per-instance — for example, a `SCHEMA_VALIDATION_FAILED` is conceptually non-retryable (the request is malformed and a retry will fail identically), but a transient `INTERNAL_ERROR` is conceptually retryable. Buyer agents MUST honor the value the dealer returns rather than the table default.

## Per-code semantics

### `UNSUPPORTED_SKILL`

Returned when a buyer agent calls a skill id the dealer agent does not implement. AAP v1.3.0 agents declare the subset of the five skills they implement (at least one) on their agent card, so buyer agents SHOULD check the declared skills before calling. This code also covers forward-compat scenarios where future AAP versions add skills not present in v1.3.0.

### `SCHEMA_VALIDATION_FAILED`

Returned when the request body does not satisfy the AAP request schema (missing `type`, wrong field types, unknown filter keys, unknown enum values, etc.). `details.errors[]` MUST list **all** failing fields at once — each with its `instanceLocation`, `keyword`, and `error` — so the buyer agent can correct the entire payload in a single retry.

### `MISSING_REQUIRED_FIELD`

Returned when a specifically required field is absent. This overlaps with `SCHEMA_VALIDATION_FAILED`; dealer agents MAY use either, but `MISSING_REQUIRED_FIELD` is preferred when the issue is a single missing required field rather than a structural validation problem (e.g. a `lead.submit` request whose `appointment.appointment_type` is `test_drive` but no `vehicle_of_interest` is provided).

### `INVALID_CONDITION`

Returned when the `condition` value is set to an item from the wrong vocabulary for its context: `vehicle_of_interest.condition` MUST be one of `new` | `used` | `cpo`, and `trade_in.condition` MUST be one of `excellent` | `good` | `fair` | `poor`. The base `Vehicle` schema accepts the union of both vocabularies because the same shape is used in inventory results too; `lead.submit` enforces the per-context subset and rejects with `INVALID_CONDITION` when violated.

### `VEHICLE_NOT_FOUND`

Returned by `inventory.vehicle` (or by `lead.submit` when `vehicle_of_interest` does not match) when none of the supplied identifiers (`vin`, `stock`, `vehicle_id`, year+make+model) match a listing.

### `VEHICLE_UNAVAILABLE`

Returned when the listing existed but is no longer available — e.g. its `status` moved out of the `available` | `intransit` | `pending` set between an `inventory.search` snapshot and a follow-up `inventory.vehicle` call. Buyer agents SHOULD recompute search results and surface the change to the user.

### `CONTACT_CONSENT_REQUIRED`

Returned when a `lead.submit` request omits `consent`, or when the `consent.allowed_channels[]` does not include the channel the dealer's process needs to use for follow-up. See [Behavior rules](https://autoagentprotocol.org/docs/v1.3/behavior-rules.md).

### `INVALID_CONSENT`

Returned when `consent` is structurally present but unusable: e.g. `consent.scope[]` is not exactly `["lead_submission"]`, or `consent.granted_at` is in the future, or `consent_text` is empty.

### `APPOINTMENT_TIME_UNAVAILABLE`

Returned by `lead.submit` when an `appointment` block was included, the requested `appointment_at` cannot be honored, AND the dealer has no `proposed_times` to offer. Dealers SHOULD prefer `data.appointment.status: "proposed"` with alternative times over this error whenever possible. The lead itself MAY still be `received` even when the appointment portion fails.

### `IDEMPOTENCY_CONFLICT`

Returned by `lead.submit` when an `idempotency_key` is reused with a request payload that differs from the original submission. A retry with the *same* key AND the *same* payload MUST be treated as idempotent — the dealer returns the original lead (e.g. `data.status: "duplicate"`), not this error. `retryable: false`: the buyer agent must use a fresh `idempotency_key` for a genuinely new request.

### `RATE_LIMITED`

Returned when the buyer agent exceeds the dealer's per-key quota. `retryable: true` is the default. Dealer agents SHOULD include `details.retry_after_ms` (or `details.retry_after_seconds`) so the buyer agent can back off appropriately. AAP does not standardize the rate-limit values themselves.

### `INTERNAL_ERROR`

A catch-all for unexpected dealer-side failures. `retryable: true` is the default; the buyer agent SHOULD retry with exponential backoff. Dealer agents SHOULD set `error_id` so support tickets can correlate to logs.

## Example error payloads

### JSON-RPC error response

```json
{
  "jsonrpc": "2.0",
  "id": "req-3",
  "error": {
    "code": -32602,
    "message": "Invalid params: filters.year_min must be an integer",
    "data": {
      "type": "aap.error",
      "error_id": "err_01HZ9EXAMPLE",
      "code": "SCHEMA_VALIDATION_FAILED",
      "message": "request failed validation with 2 errors",
      "retryable": false,
      "details": {
        "errors": [
          { "instanceLocation": "/filters/year_min", "keyword": "type", "error": "must be an integer" },
          { "instanceLocation": "/filters/make", "keyword": "additionalProperties", "error": "unknown filter key" }
        ]
      },
      "created_at": "2026-04-30T10:15:30Z"
    }
  }
}
```

### Consent-required example

A `lead.submit` request with `customer` but no `consent`:

```json
{
  "type": "aap.error",
  "error_id": "err_01HZ9CONSENT01",
  "code": "CONTACT_CONSENT_REQUIRED",
  "message": "Customer info present but no ConsentGrant. Provide a 'consent' block with scope ['lead_submission'].",
  "retryable": false,
  "details": {
    "missing": "consent",
    "expected_scope": "lead_submission"
  },
  "created_at": "2026-04-30T10:15:45Z"
}
```

### Rate-limit example

```json
{
  "type": "aap.error",
  "error_id": "err_01HZ9RATE01",
  "code": "RATE_LIMITED",
  "message": "Per-key rate limit exceeded.",
  "retryable": true,
  "details": {
    "retry_after_ms": 30000
  },
  "created_at": "2026-04-30T10:16:00Z"
}
```

## What dealer agents MUST and MUST NOT do

- Dealer agents MUST return errors using this schema (typed `aap.error` payload), not free-text messages.
- Dealer agents MUST NOT leak internal stack traces in `message`. Use `details` for structured diagnostic information that is safe to display.
- Dealer agents MUST set `retryable` truthfully. Buyer agents follow this signal to decide whether to retry.
