feat(server/a2a): structured error parity with MCP — emit field/details/retry_after, catch decisioning AdcpError#536
Merged
Conversation
…ls/retry_after, catch decisioning AdcpError (closes #530) Mirrors PR #525's MCP-side fix on the A2A surface. Two parity gaps closed: 1. ``ADCPAgentExecutor.execute()`` now catches both ``adcp.exceptions.ADCPError`` AND ``adcp.decisioning.types.AdcpError``. The two are disjoint hierarchies; pre-fix decisioning errors fell into the generic ``except Exception`` and rendered as plain "Skill execution failed" text — losing the structured shape entirely for adopters using ``DecisioningPlatform``. 2. ``_send_adcp_error`` projects the full envelope: ``code``, ``message``, ``recovery``, ``field``, ``suggestion``, ``retry_after``, ``details``. Pre-fix only ``code`` / ``message`` / ``recovery`` / ``suggestion`` reached the wire; ``field`` / ``details`` / ``retry_after`` were dropped even when the raised error supplied them. Field extraction is shared with the MCP path via ``adcp.server.translate._extract_structured_fields`` (the helper #525 factored out) so both transports project off the same source-of-truth shape — no duplication, no drift. Tests parametrized over the same code set as #525's MCP test (``MEDIA_BUY_NOT_FOUND``, ``PACKAGE_NOT_FOUND``, ``TERMS_REJECTED``, ``BUDGET_TOO_LOW``) plus optional-field gating coverage and an ``ADCPTaskError`` regression case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #530. Mirrors the MCP-side fix from #525 onto the A2A surface so storyboards graded against A2A-served sellers see the same structured
adcp_errorenvelope as MCP buyers. Without this, A2A surfaces would surface a wire-conformance gap once the storyboard runner's structured-error checks run against A2A (or via the upstream storyboard runner UX work in adcp-client#1527).Two parity gaps closed
1. Catch both exception hierarchies.
ADCPAgentExecutor.execute()now catches bothadcp.exceptions.ADCPError(client-side framework error) ANDadcp.decisioning.types.AdcpError(server-side structured error platform methods raise). They are disjoint hierarchies — pre-fix the decisioning case fell intoexcept Exceptionand rendered as plain "Skill execution failed" text, losing the structured shape entirely for adopters usingDecisioningPlatform.2. Emit the full envelope.
_send_adcp_errornow projectscode,message,recovery,field,suggestion,retry_after,details— populated when the raised exception supplies them, omitted whenNone. Pre-fix onlycode/message/recovery/suggestionreached the wire.Reuse, not duplication
Field extraction is shared with the MCP path via
adcp.server.translate._extract_structured_fields(the helper #525 factored out for exactly this purpose). Both transports project off the same source-of-truth shape — no drift between MCP and A2A error envelopes._extract_structured_fieldswas reusable as-is — no factoring needed.Test plan
MEDIA_BUY_NOT_FOUND,PACKAGE_NOT_FOUND,TERMS_REJECTED,BUDGET_TOO_LOW)AdcpErrorreaches A2ADataPartas structured envelope (the main parity gap — was dropped pre-fix)field,details,retry_after,suggestion) populate when present, omit when absentADCPTaskError(the only path that worked pre-fix) regression: still caught, still projects structured envelope,fieldpopulates when carried onError.fieldtests/test_a2a_server.pysuite stays green (40 tests)ruff check+mypy src/adcp/server/cleanRefs
_extract_structured_fieldsdirectly🤖 Generated with Claude Code