Skip to content

feat(client): surface structured adcp_error on TaskResult#675

Merged
bokelley merged 1 commit into
mainfrom
claude/task-result-adcp-error
May 11, 2026
Merged

feat(client): surface structured adcp_error on TaskResult#675
bokelley merged 1 commit into
mainfrom
claude/task-result-adcp-error

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

Closes the SDK's last politeness move on the buyer error path: TaskResult.adcp_error is now a first-class field carrying the seller's full structured error envelope, independent of debug mode.

Previously the SDK paraphrased a seller's structured adcp_error (code, field, details, recovery, ...) into a flat error: str and gated the structured form behind debug=True. That made spec-shaped errors invisible to alerting, version-compat probes, and grader assertions in production.

This is the Python analog of the JS-side issue 1679 ("flatten wire INVALID_REQUEST detail to {}") — same disease, slightly different shape: Python doesn't flatten to {}, it flattens to a string and hides the structure behind a debug flag.

What changes

  • TaskResult.adcp_error: dict[str, Any] | None — new public field, populated whenever the wire carried a spec-shaped envelope.
  • MCP: FAILED-path TaskResult carries the already-validated adcp_error dict (was previously only in debug_info.response).
  • A2A: FAILED-path now extracts the adcp_error DataPart per transport-errors.mdx §A2A Binding. When the seller omits the TextPart, the structured envelope's message/code synthesizes the error string so adopters don't see the "Task failed" placeholder mask a real diagnostic.
  • Shared validator: protocols/_adcp_errors.py holds the spec-defined size/shape caps (code non-empty string ≤ 64 chars, total ≤ 4 KB), used by both transports.

Adopter ergonomics

result = await client.create_media_buy(...)
if result.adcp_error and result.adcp_error["code"] == "INVALID_REQUEST":
    field = result.adcp_error.get("field")
    ...

The flat error: str stays for back-compat (human messages, logging).

Tests

  • MCP: structuredContent.adcp_error and text-fallback both surface on TaskResult.adcp_error with debug=False.
  • A2A: failed task with adcp_error DataPart surfaces structurally; envelope-only (no TextPart) synthesizes the human message.
  • Existing "no structured envelope" tests gained an assertion that adcp_error stays None.

Test plan

  • pytest tests/test_protocols.py tests/test_mcp_structured_error.py tests/test_a2a_structured_error.py tests/test_mcp_extraction.py — 194 passed
  • Broader sweep including idempotency, error-code-conformance, translate, capabilities — 361 passed
  • ruff check clean on changed files
  • mypy clean on changed files
  • Public API snapshot tests still pass

🤖 Generated with Claude Code

TaskResult.error is for humans; TaskResult.adcp_error is for programs.
Both MCP and A2A failure paths now populate the structured envelope
(code, message, recovery, field, suggestion, retry_after, details)
independent of debug mode, so adopters can branch on adcp_error.code
without enabling debug capture.

Previously the SDK paraphrased the seller's structured diagnostic into
a flat error string and gated the structured form behind debug=True.
That hid spec-shaped errors (INVALID_REQUEST.field_path, TERMS_REJECTED,
etc.) from alerting, version-compat probes, and grader assertions.

- TaskResult.adcp_error: dict[str, Any] | None added to the public model
- MCP FAILED path passes the extracted envelope through
- A2A FAILED path now extracts the adcp_error DataPart per
  transport-errors.mdx §A2A Binding; falls back to envelope.message /
  envelope.code when the seller omits the TextPart
- Validation helper extracted to protocols/_adcp_errors.py so both
  transports share the spec-defined size/shape caps

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit fe66335 into main May 11, 2026
16 checks passed
@bokelley bokelley deleted the claude/task-result-adcp-error branch May 11, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant