Thomas Piacentini
← Atelier

Chapitre

Errors

Texte original · anglais

Nine A2A errors map into three bindings, and because their status codes collide, the ErrorInfo reason in the details is what tells them apart.

The planner asks test-runner for a task id that does not exist, once over each JSON binding. JSON-RPC answers HTTP/1.1 200 OK with code -32001, and HTTP+JSON answers HTTP/1.1 404 Not Found. Both carry the same ErrorInfo, with the reason TASK_NOT_FOUND, as figure shows.

When you finish this section, you can read an error in any binding, name the A2A error behind it, and choose what to do next.

Fig. 5.3TaskNotFoundError in two JSON bindingscomparison
The same TaskNotFoundError arrives as HTTP 200 with code -32001 in JSON-RPC and as HTTP 404 in HTTP+JSON, carrying the same ErrorInfo.
The same TaskNotFoundError arrives as HTTP 200 with code -32001 in JSON-RPC and as HTTP 404 in HTTP+JSON, carrying the same ErrorInfo. Left, GetTask over JSON-RPC from the error capture. Right, the same lookup over HTTP+JSON from the HTTP+JSON capture. The box at the bottom is the detail both replies carry. Ids shortened to 8 characters.

Original SVG

Original source
id: fig-task-not-found
kind: comparison
title: TaskNotFoundError in two JSON bindings
claim: The same TaskNotFoundError arrives as HTTP 200 with code -32001 in JSON-RPC and as HTTP 404 in HTTP+JSON, carrying the same ErrorInfo.
caption: Left, GetTask over JSON-RPC from the error capture. Right, the same lookup over HTTP+JSON from the HTTP+JSON capture. The box at the bottom is the detail both replies carry. Ids shortened to 8 characters.

The detail that names the error

The specification defines nine A2A errors and maps each one into every binding {{spec §3.3.2}} {{spec §5.4}}. JSON-RPC numbers them -32001 to -32009 {{spec §9.5}}, HTTP answers 400 for seven, and gRPC answers FAILED_PRECONDITION for six, as the error table lists.

Every error carries a code, a message, and optional details, each detail naming its type in @type {{spec §3.3.2}}. For A2A errors the deciding detail is a google.rpc.ErrorInfo:

Original source
label: the detail that names the error
source: spec §11.6
---
For an A2A error, an implementation must put a `google.rpc.ErrorInfo` object in the `details` array.

Its reason is the error's name in upper snake case without the Error suffix, such as TASK_NOT_FOUND, and its domain is a2a-protocol.org {{spec §11.6}}. The gRPC binding carries the same MUST {{spec §10.6}}, and JSON-RPC only a SHOULD {{spec §9.5}}. The metadata is free-form: the kit fills it, and the reference SDK leaves it empty, as the kit README lists.

JSON-RPC errors arrive with HTTP 200

A JSON-RPC error is an ordinary response with HTTP 200. Its error member holds code, message, and data where result would be {{spec §9.5}}. The five standard JSON-RPC codes, -32700 and -32600 to -32603, keep their meaning {{spec §9.5}}.

Validation errors carry a different detail type, a google.rpc.BadRequest that names the field:

a message without messageId, refused with a BadRequest detailhttpFull source
    "message": {
      "role": "ROLE_USER",
…
HTTP/1.1 200 OK
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 4,
  "error": {
    "code": -32602,
    "message": "Invalid parameters",
    "data": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "message.messageId",
            "description": "messageId is required"
The request is cut to the start of its message, which has no messageId.
Original source
title: a message without messageId, refused with a BadRequest detail
source: capture/out/11-errors.http
lang: http
note: The request is cut to the start of its message, which has no messageId.
---
    "message": {
      "role": "ROLE_USER",
…
HTTP/1.1 200 OK
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": 4,
  "error": {
    "code": -32602,
    "message": "Invalid parameters",
    "data": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "message.messageId",
            "description": "messageId is required"

Do not match on message texts: the reference SDK answers the same case with "Validation failed" and names the field message.message_id {{sdk src/a2a/utils/proto_utils.py}}.

Authentication can fail before JSON-RPC runs, with HTTP 401 as the specification's example {{spec §3.3.2}}, so read the status first, as Security schemes shows.

HTTP+JSON errors are google.rpc.Status

An HTTP+JSON error uses the HTTP status and a JSON google.rpc.Status {{spec §11.6}}. Its error object repeats the status in code, names the gRPC status in status, and adds message and details:

a cancel of a completed task over HTTP+JSONhttpFull source
POST /a2a/rest/tasks/bbfb9ea5-50ea-4441-b991-2a8ba0d6f3f3:cancel HTTP/1.1
…
HTTP/1.1 400 Bad Request
Content-Type: application/a2a+json

{
  "error": {
    "code": 400,
    "status": "FAILED_PRECONDITION",
    "message": "Task cannot be canceled",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "TASK_NOT_CANCELABLE",
        "domain": "a2a-protocol.org",
        "metadata": {
          "taskId": "bbfb9ea5-50ea-4441-b991-2a8ba0d6f3f3",
          "state": "TASK_STATE_COMPLETED"
The request headers and body are cut. Over JSON-RPC this error is code -32002, as in 10-cancel.http.
Original source
title: a cancel of a completed task over HTTP+JSON
source: capture/out/16-rest-binding.http
lang: http
note: The request headers and body are cut. Over JSON-RPC this error is code -32002, as in 10-cancel.http.
---
POST /a2a/rest/tasks/bbfb9ea5-50ea-4441-b991-2a8ba0d6f3f3:cancel HTTP/1.1
…
HTTP/1.1 400 Bad Request
Content-Type: application/a2a+json

{
  "error": {
    "code": 400,
    "status": "FAILED_PRECONDITION",
    "message": "Task cannot be canceled",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "TASK_NOT_CANCELABLE",
        "domain": "a2a-protocol.org",
        "metadata": {
          "taskId": "bbfb9ea5-50ea-4441-b991-2a8ba0d6f3f3",
          "state": "TASK_STATE_COMPLETED"

Two samples in the specification still show application/problem+json bodies in the older RFC 9457 style {{spec §6.4}} {{spec §6.5}} (conflict D13). PR #1641 and PR #1689 replace those samples, and both are still open. The migration guide gives the type as application/json {{docs whats-new-v1}}, where §11.6 shows application/a2a+json. The SDKs send all three, and all answer JSON-RPC errors with HTTP 200:

SDK JSON-RPC errors HTTP+JSON errors
a2a-python 1.2.2 application/json {{sdk src/a2a/server/routes/jsonrpc_dispatcher.py}} application/json {{sdk src/a2a/utils/error_handlers.py}}
a2a-go application/json {{sdk-go a2asrv/jsonrpc.go at v2.6.0}} application/json {{sdk-go a2asrv/rest.go at v2.6.0}}
a2a-java application/json {{sdk-java reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java at v1.4.0.Final}} application/json {{sdk-java transport/rest/src/main/java/org/a2aproject/sdk/transport/rest/handler/RestHandler.java at v1.4.0.Final}}
a2a-js application/json {{sdk-js src/server/express/json_rpc_handler.ts at v1.3.0}} application/a2a+json {{sdk-js src/server/express/rest_handler.ts at v1.3.0}}
a2a-dotnet application/json {{sdk-dotnet src/A2A.AspNetCore/JsonRpcResponseResult.cs at v1.0.0-preview2}} application/problem+json, a problem-details body {{sdk-dotnet src/A2A.AspNetCore/A2AHttpProcessor.cs at v1.0.0-preview2}}
a2a-rs application/json {{sdk-rust a2a-server/src/jsonrpc.rs at a2a-server-lf-v0.5.1}} application/problem+json {{sdk-rust a2a-server/src/rest.rs at a2a-server-lf-v0.5.1}}

Send application/a2a+json, and accept all three types when you read. Parse the body as google.rpc.Status whatever the type says. The exception is a2a-dotnet at this release, whose problem-details body carries no ErrorInfo. The gRPC binding puts the same three parts in status.code, status.message, and status.details {{spec §10.6}}.

What to retry

UnsupportedOperationError covers three problems with one code, -32004, and one reason, UNSUPPORTED_OPERATION {{spec §3.3.4}} {{spec §3.1.1}} {{spec §3.1.6}}. The card lacks streaming or the extended card, or the task is terminal, as in 10-cancel.http. Only message and metadata tell the cases apart, and the specification fixes neither. So when a task operation gets -32004, read the task with GetTask before you decide.

Most A2A errors describe the request or the task, so resending the same bytes gets the same answer:

  • -32603, HTTP 500 or 503, or a dropped connection: retry with backoff, and honor Retry-After {{spec §3.3.2}}.
  • HTTP 401: get a fresh credential, then retry.
  • -32600, -32602, -32005, -32009, and their 400 forms: fix the request before you send it again.
  • -32001: stop, because the id is wrong, purged, or hidden from you {{spec §3.3.2}}.
  • -32002, or -32004 on a terminal task: start a new task, as CancelTask and terminal states shows.
  • -32003, -32007, or -32004 from a capability check: read the card again.

A retried SendMessage can start a second task, because duplicate detection by messageId is optional {{spec §3.3.1}}. Send the same messageId anyway, as Message explains.

Original source
- Read the HTTP status first, then the JSON-RPC `error` member or the `google.rpc.Status` body.
- Decide on the `ErrorInfo` `reason`, never on the status code alone or on the message text.
- Retry only transport and system errors, with backoff.
- Keep the same `messageId` when you retry a send.

Sources: spec §3.1.1, §3.1.6, §3.3.1, §3.3.2, §3.3.4, §5.4, §6.4, §6.5, §9.5, §10.6, §11.6 (research/sources/specification.md); docs whats-new-v1 (research/sources/docs.md); a2aproject/A2A PR #1641 and PR #1689, checked 2026-10-06; sdk src/a2a/utils/proto_utils.py, src/a2a/utils/error_handlers.py, src/a2a/server/routes/jsonrpc_dispatcher.py (a2a-python 1.2.2); sdk-go a2asrv/jsonrpc.go, a2asrv/rest.go (a2a-go v2.6.0); sdk-java reference/jsonrpc/src/main/java/org/a2aproject/sdk/server/apps/quarkus/A2AServerRoutes.java, transport/rest/src/main/java/org/a2aproject/sdk/transport/rest/handler/RestHandler.java (a2a-java v1.4.0.Final); sdk-js src/server/express/json_rpc_handler.ts, src/server/express/rest_handler.ts (a2a-js v1.3.0); sdk-dotnet src/A2A.AspNetCore/JsonRpcResponseResult.cs, src/A2A.AspNetCore/A2AHttpProcessor.cs (a2a-dotnet v1.0.0-preview2); sdk-rust a2a-server/src/jsonrpc.rs, a2a-server/src/rest.rs (a2a-rs a2a-server-lf-v0.5.1); capture/out/11-errors.http, 16-rest-binding.http, 10-cancel.http, 14-resubscribe.http; capture/README.md

Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓