Why architecture matters here
A2A error handling matters because cross-agent failures are inevitable and harder to handle than in-process failures, and robust handling is what makes multi-agent systems reliable rather than fragile. When agents delegate tasks to other agents (A2A), the failures cross agent boundaries -- between independent agents, over a network, possibly across organizations. These failures are inevitable (network issues, agent failures, task rejections, timeouts -- all happen at scale) and harder than in-process failures (the agents are independent -- you can't just catch an exception; you must handle structured cross-agent errors, partial failures across agents, unresponsive agents, and the inability to simply roll back across independent parties). Without robust error handling, multi-agent systems are fragile (a failure in one delegated task breaking the whole workflow, or leaving inconsistent state -- some steps done, some not). With robust handling (structured errors, retries, compensation, propagation), multi-agent systems are reliable (handling the cross-agent failures gracefully -- recovering, compensating, surfacing errors meaningfully). For building reliable multi-agent systems (the direction agentic systems are heading -- agents collaborating via A2A), robust error handling is essential, and understanding it is understanding how to make cross-agent collaboration reliable.
The structured-errors insight is foundational, and it's what makes cross-agent failures programmatically handleable. When an agent delegates a task and it fails, the calling agent needs to understand the failure to react appropriately (retry? give up? try a different agent? compensate?). An opaque error (just a string, or a generic failure) doesn't enable this (the caller can't programmatically distinguish a transient network error -- retry -- from a permanent rejection -- don't retry -- from an invalid-input error -- fix the input). Structured errors solve this: an error with a machine-readable code (a specific error code the caller can programmatically match -- e.g., 'rate_limited', 'invalid_input', 'task_rejected', 'internal_error'), a human-readable message (for logging/debugging), and optional data (structured details -- e.g., which input field was invalid, when to retry). So the calling agent can programmatically react to the specific error (retry on 'rate_limited' after the indicated delay; fix and retry on 'invalid_input'; give up or try another agent on 'task_rejected') -- appropriate handling per error type. This structured approach (codes for programmatic handling, messages for humans, data for details) is the same as well-designed APIs (structured error responses -- e.g., JSON-RPC error objects, which A2A builds on) -- and it's foundational to cross-agent error handling (enabling the caller to understand and react to failures programmatically). Understanding that structured errors (code/message/data) enable programmatic reaction to specific failures is understanding the foundation of A2A error handling.
And the partial-failure-plus-compensation reality is the hardest and most important aspect, because you can't simply roll back across independent agents. In a multi-agent workflow (an agent orchestrating several sub-agents' tasks -- e.g., book a flight via one agent, a hotel via another, a car via a third), a partial failure is a serious concern: some steps succeed (the flight and hotel booked) and a later step fails (the car booking fails) -- leaving a partial, inconsistent state (flight and hotel booked, but no car -- an incomplete trip). Unlike a single database transaction (which you can roll back atomically), you can't simply roll back across independent agents (the flight and hotel agents have committed their bookings -- there's no cross-agent transaction to abort). So the handling is compensation (the saga pattern): to undo the completed steps, you issue compensating actions (cancel the flight booking, cancel the hotel booking -- explicit undo actions, since you can't roll back) -- returning the system to a consistent state (nothing booked) after the partial failure. This is fundamentally different from in-process error handling (where a transaction rolls back automatically) -- across independent agents, you must explicitly compensate (issue undo actions for the completed steps). So partial failure (some steps done, some failed -- an inconsistent state) is handled by compensation (explicit undo actions -- the saga pattern -- since you can't roll back across agents) -- the hardest and most important aspect of A2A error handling (managing consistency across independent agents despite partial failures). Understanding partial failure and compensation (the saga pattern for cross-agent consistency) is understanding the crux of A2A error handling.
The architecture: every piece explained
Top row: error structure and states. Error types: the categories of errors -- protocol errors (malformed requests, protocol violations), task errors (the task failed -- rejected, failed partway), agent errors (the agent itself failed -- internal error, unavailable) -- the taxonomy. Structured errors: errors with a machine-readable code (for programmatic handling), a human-readable message (for logging/debugging), and optional data (structured details) -- enabling the caller to react appropriately. Task failure states: a delegated task's failure outcomes -- failed (the task attempted but failed), cancelled (the task cancelled -- by the caller or agent), rejected (the agent declined the task) -- distinct states the caller handles differently. Retries and idempotency: safely re-attempting failed tasks (retrying transient failures) -- requiring idempotency (so a retry doesn't duplicate effects -- e.g., an idempotency key so a retried booking doesn't double-book).
Middle row: resilience mechanisms. Timeouts: handling unresponsive agents (not waiting forever for a response -- timing out and treating it as a failure) -- so an unresponsive agent doesn't hang the caller. Partial failure: in a multi-agent workflow, some steps succeed and some fail -- a partial, inconsistent state to handle. Compensation: undoing the completed steps when a later step fails (the saga pattern -- issuing compensating actions, since you can't roll back across independent agents) -- restoring consistency. Error propagation: surfacing errors meaningfully to the original caller (not swallowing them -- propagating the structured error up so the caller/user understands what failed and why) -- meaningful failure surfacing.
Bottom rows: isolation and visibility. Circuit breaking: isolating a persistently-failing agent (a circuit breaker -- after repeated failures, stop calling the failing agent for a while -- not hammering it, and failing fast) -- protecting against a failing agent. Observability: tracing errors across the agents (a failure spans multiple agents -- so distributed tracing across the agent calls is needed to understand where and why it failed) -- visibility into cross-agent failures. The ops strip: error taxonomy (a well-defined taxonomy of errors -- codes and their meanings -- so errors are consistent and programmatically handleable across the agents), retry policy (the retry policy -- what to retry -- transient errors -- how many times, with what backoff, requiring idempotency -- for safe, effective retries), and monitoring (monitoring the cross-agent errors -- error rates per agent/type, failures, circuit-breaker states -- for reliability and troubleshooting).
End-to-end flow
Trace a multi-agent workflow with a partial failure and compensation. An orchestrating agent books a trip: it delegates to a flight agent (books the flight -- succeeds), a hotel agent (books the hotel -- succeeds), and a car agent (books the car -- but this fails, returning a structured error -- code 'no_availability'). Now there's a partial failure (flight and hotel booked, car failed -- an inconsistent, incomplete trip). The orchestrator handles it: since it can't roll back across the independent agents (the flight and hotel are booked -- committed by those agents), it compensates -- issuing compensating actions (cancel the flight booking via the flight agent, cancel the hotel booking via the hotel agent) -- undoing the completed steps. After compensation, the system is consistent again (nothing booked -- the partial state undone). The orchestrator then propagates the failure to the user (a meaningful error -- 'trip booking failed: no car availability; the flight and hotel bookings were cancelled'). The partial failure (car booking failing after flight/hotel succeeded) was handled by compensation (explicit undo of the completed bookings -- the saga pattern) and meaningful propagation -- maintaining consistency despite the cross-agent partial failure.
The structured-error and retry vignettes show the programmatic handling. A structured-error case: the flight agent returns a structured error (code 'rate_limited', data indicating retry-after 5 seconds). The orchestrator programmatically handles it (recognizing the 'rate_limited' code as transient and retryable -- waiting the indicated 5 seconds and retrying) -- appropriate handling based on the structured error (versus an opaque error, where it couldn't know to retry). Had the code been 'invalid_input' (a permanent error), it would handle it differently (fix the input or give up -- not retry) -- the structured code enabling the right reaction. A retry case: the orchestrator retries the rate-limited flight booking -- and crucially, the booking is idempotent (an idempotency key ensures the retry doesn't double-book -- if the first attempt actually succeeded despite the rate-limit response, the retry with the same key doesn't create a duplicate) -- so the retry is safe (no duplicate booking). The idempotency made the retry safe (no duplicated effect).
The timeout and circuit-breaking vignettes complete it. A timeout case: the hotel agent becomes unresponsive (no response). The orchestrator times out (not waiting forever -- treating the unresponsive agent as a failure after the timeout) -- so the workflow doesn't hang on the unresponsive agent (it handles the timeout as a failure -- retrying, trying another agent, or compensating). A circuit-breaking case: an agent starts persistently failing (every call to it failing). The orchestrator's circuit breaker opens (after repeated failures, it stops calling the failing agent for a while -- failing fast instead of hammering it) -- protecting against the failing agent (not wasting time/resources on a dead agent, and giving it time to recover). The consolidated discipline the team documents: use structured errors (code/message/data -- for programmatic handling), define task failure states (failed/cancelled/rejected -- distinct outcomes), retry transient failures safely (with idempotency -- so retries don't duplicate effects) per a retry policy, handle timeouts (unresponsive agents -- not waiting forever), handle partial failures with compensation (the saga pattern -- explicit undo actions, since you can't roll back across agents), propagate errors meaningfully (surface to the caller -- not swallow), use circuit breaking (isolate persistently-failing agents), and observe errors across agents (distributed tracing) with a clear error taxonomy and monitoring -- because cross-agent failures are inevitable and harder than in-process failures (structured errors, partial failures, compensation across independent agents), and robust A2A error handling (structured errors, retries/idempotency, compensation, propagation) is what makes multi-agent systems reliable rather than fragile.