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

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` for JSON-RPC ([Section 9.5](https://autoagentprotocol.org/docs/v0.2/bindings/json-rpc.md#error-mapping-a2a-section-95)) or as a `DataPart` in a non-2xx HTTP response for HTTP+JSON ([Section 11.6](https://autoagentprotocol.org/docs/v0.2/bindings/rest.md#error-mapping-a2a-section-116)).

## Error payload shape

```json
{
  "type": "aap.error",
  "error_id": "err_01HZ9EXAMPLE",
  "code": "SCHEMA_VALIDATION_FAILED",
  "message": "filters.year_min must be an integer",
  "retryable": false,
  "details": {
    "instancePath": "/filters/year_min",
    "received": "twenty-twenty"
  },
  "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 11 AAP error codes below. |
| `message` | string | yes | Human-readable error message. 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; free shape (e.g. validation paths, retry hints). |
| `created_at` | date-time | yes | When the error was generated by the dealer agent. |

## Error code reference

The 11 codes, their meaning, recommended HTTP status, recommended JSON-RPC code, and `retryable` default.

| `code` | Meaning | HTTP | JSON-RPC | `retryable` default |
|---|---|---|---|---|
| `UNSUPPORTED_SKILL` | The agent does not implement this skill. | 404 | -32601 (Method not found) | `false` |
| `SCHEMA_VALIDATION_FAILED` | Request body fails JSON Schema validation. | 422 | -32602 (Invalid params) | `false` |
| `MISSING_REQUIRED_FIELD` | A specifically required field is absent. | 422 | -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. | 422 | -32602 (Invalid params) | `false` |
| `VEHICLE_NOT_FOUND` | The supplied `vin` / `stock` / `vehicle_id` does not match any listing. | 404 | -32000 (Server error) | `false` |
| `VEHICLE_UNAVAILABLE` | The vehicle exists but its `status` is no longer one of `available` \| `intransit` \| `pending`. | 409 | -32000 (Server error) | `false` |
| `CONTACT_CONSENT_REQUIRED` | `customer` info present without `consent`, or follow-up channel not in `consent.allowed_channels`. | 403 | -32000 (Server error) | `false` |
| `INVALID_CONSENT` | `consent` is present but malformed, expired, or its scope does not cover the called skill. | 403 | -32000 (Server error) | `false` |
| `APPOINTMENT_TIME_UNAVAILABLE` | The requested `appointment_at` cannot be honored AND the dealer has no proposed alternatives. | 409 | -32000 (Server error) | `false` |
| `RATE_LIMITED` | Client has exceeded the dealer's rate limit. | 429 | -32002 (Server error reserved) | **`true`** |
| `INTERNAL_ERROR` | Unhandled dealer-side error. | 500 | -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-compliant dealers implement all five skills, so this code is rare in practice. It is intended for forward-compat scenarios where future AAP versions add skills not present in v0.2.

### `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.instancePath` SHOULD point at the failing field.

### `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/v0.2/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.

### `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": "filters.year_min must be an integer",
      "retryable": false,
      "details": {
        "instancePath": "/filters/year_min",
        "received": "twenty-twenty"
      },
      "created_at": "2026-04-30T10:15:30Z"
    }
  }
}
```

### HTTP+JSON error response

A2A v1.0 (Section 11.6) uses Google's standard `google.rpc.Status` shape for HTTP error bodies. AAP places the typed `aap.error` payload as one of the entries in `error.details[]`, alongside a `google.rpc.ErrorInfo` entry whose `reason` carries the AAP code.

```http
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "error": {
    "code": 422,
    "message": "filters.year_min must be an integer",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "SCHEMA_VALIDATION_FAILED",
        "domain": "autoagentprotocol.org",
        "metadata": {
          "instancePath": "/filters/year_min",
          "received": "twenty-twenty"
        }
      },
      {
        "@type": "type.googleapis.com/aap.error",
        "type": "aap.error",
        "error_id": "err_01HZ9EXAMPLE",
        "code": "SCHEMA_VALIDATION_FAILED",
        "message": "filters.year_min must be an integer",
        "retryable": false,
        "details": {
          "instancePath": "/filters/year_min",
          "received": "twenty-twenty"
        },
        "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.
