Goal: Make a2a failures machine-readable under -o json, so a program can handle an error the same way it handles a result.
User story: As a program or agent consuming -o json, I want failures returned as a JSON object just like successes, so that I can detect and branch on errors with a single JSON parse instead of scraping stderr.
Priority: P0 · Area: CLI code
Problem
On failure the tool prints a human-readable Error: <text> line to stderr — even when the caller asked for -o json. A program gets a parseable object on success but an unparseable plaintext line on failure.
$ a2a card get http://127.0.0.1:9999 -o json # nothing listening
Error: failed to resolve agent card: ... connect: connection refused
$ a2a send --bogus -o json 'x'
Error: unknown flag: --bogus
Proposed behavior
Under -o json, a failure emits exactly one error object on stdout (diagnostics stay on stderr):
{
"error": {
"code": "string",
"message": "human-readable string",
"hint": "actionable next step, or null",
"a2aCode": "underlying transport code, or null"
}
}
code — a protocol error carries the agent's error name (e.g. TaskNotFoundError); a CLI-local condition carries a symbolic A2ACLI_ERR_* code.
- Proposed CLI-local codes:
A2ACLI_ERR_USAGE, A2ACLI_ERR_CARD_NOT_FOUND, A2ACLI_ERR_CARD_INVALID, A2ACLI_ERR_UNREACHABLE, A2ACLI_ERR_CREDENTIALS_MISSING, A2ACLI_ERR_AUTH_FAILED, A2ACLI_ERR_TIMEOUT, A2ACLI_ERR_INTERNAL.
hint — a short, copy-pasteable next step when one exists, else null.
Examples:
{"error":{"code":"A2ACLI_ERR_CARD_NOT_FOUND","message":"no agent card at http://127.0.0.1:9999","hint":"check the --agent-card reference","a2aCode":null}}
{"error":{"code":"A2ACLI_ERR_USAGE","message":"unknown flag: --bogus","hint":"run: a2a send --help","a2aCode":null}}
Under --stream, an error that terminates the stream is emitted as a final error object on its own line. text mode may keep the readable Error: line.
Acceptance criteria
Environment
- CLI version:
a2a version v0.0.0-…1e29dfe94f95+dirty
- OS: Linux x86_64
Notes
Pairs with the distinct-exit-codes request — both live in Execute()/error handling (internal/cli/root.go); the reported code and the exit code should agree.
Goal: Make
a2afailures machine-readable under-o json, so a program can handle an error the same way it handles a result.User story: As a program or agent consuming -o json, I want failures returned as a JSON object just like successes, so that I can detect and branch on errors with a single JSON parse instead of scraping stderr.
Priority: P0 · Area: CLI code
Problem
On failure the tool prints a human-readable
Error: <text>line to stderr — even when the caller asked for-o json. A program gets a parseable object on success but an unparseable plaintext line on failure.Proposed behavior
Under
-o json, a failure emits exactly one error object on stdout (diagnostics stay on stderr):{ "error": { "code": "string", "message": "human-readable string", "hint": "actionable next step, or null", "a2aCode": "underlying transport code, or null" } }code— a protocol error carries the agent's error name (e.g.TaskNotFoundError); a CLI-local condition carries a symbolicA2ACLI_ERR_*code.A2ACLI_ERR_USAGE,A2ACLI_ERR_CARD_NOT_FOUND,A2ACLI_ERR_CARD_INVALID,A2ACLI_ERR_UNREACHABLE,A2ACLI_ERR_CREDENTIALS_MISSING,A2ACLI_ERR_AUTH_FAILED,A2ACLI_ERR_TIMEOUT,A2ACLI_ERR_INTERNAL.hint— a short, copy-pasteable next step when one exists, elsenull.Examples:
{"error":{"code":"A2ACLI_ERR_CARD_NOT_FOUND","message":"no agent card at http://127.0.0.1:9999","hint":"check the --agent-card reference","a2aCode":null}} {"error":{"code":"A2ACLI_ERR_USAGE","message":"unknown flag: --bogus","hint":"run: a2a send --help","a2aCode":null}}Under
--stream, an error that terminates the stream is emitted as a final error object on its own line.textmode may keep the readableError:line.Acceptance criteria
-o jsonfailures emit the error object on stdout; nothing but structured output on stdout.A2ACLI_ERR_*code.hintpopulated where an actionable next step exists.Environment
a2a version v0.0.0-…1e29dfe94f95+dirtyNotes
Pairs with the distinct-exit-codes request — both live in
Execute()/error handling (internal/cli/root.go); the reportedcodeand the exit code should agree.