Status-Based Extraction
The extraction location depends on the task’s status:| Status | Type | Data Location | DataPart Selection |
|---|---|---|---|
completed | Final | .artifacts[0].parts[] | Last DataPart |
failed | Final | .artifacts[0].parts[] | Last DataPart |
canceled | Final | .artifacts[0].parts[] | Last DataPart (typically none) |
working | Interim | status.message.parts[] | First DataPart |
submitted | Interim | status.message.parts[] | First DataPart |
input-required | Interim | status.message.parts[] | First DataPart |
Extraction Algorithm
Clients MUST extract AdCP data from A2A responses using these steps:- Read
status.state. If absent, return null. - Final states (
completed,failed,canceled): a. Look inartifacts[0].parts[]for DataParts (kind === 'data'with non-null object.data). b. Use the last DataPart as authoritative (see Last-DataPart Authority). c. Reject wrappers: If the DataPart’s.datahas a single keyresponsecontaining an object, this is a framework wrapper bug. Throw or log an error. d. Return.data. e. Fallback: If no artifacts or no DataPart in artifacts, checkstatus.message.parts[]using step 3. - Interim states (
working,submitted,input-required): a. Look instatus.message.parts[]for DataParts. b. Use the first DataPart. c. Return.data, or null if no DataPart found. - Unknown states: Return null. Forward-compatible clients SHOULD NOT throw on unrecognized status values.
Last-DataPart Authority
For final states, the last DataPart inartifacts[0].parts[] is authoritative. During streaming, intermediate DataParts may contain stale progress data that gets superseded by the final result:
{"products": [...], "total": 12}, not {"progress": 25}.
For interim states, the first DataPart is used because interim updates are single-event snapshots, not accumulated.
Wrapper Rejection
Clients MUST reject DataParts where.data is wrapped in a framework-specific object:
.data has exactly one key named response whose value is an object, it is a wrapper. This is a server-side bug — clients should throw or log an error, not silently unwrap.
Wrapper detection applies to final states only (artifacts). Interim status messages are lightweight progress snapshots — wrapper detection is not required for status.message.parts.
Exception: A .data object that has response alongside other keys is NOT a wrapper:
Relationship to Error Extraction
This algorithm extracts any AdCP data from A2A responses, including error payloads (adcp_error). Error-specific extraction (Transport Error Mapping) is a specialization that checks for the adcp_error key in the extracted data.
The transport-errors spec provides its own extractAdcpErrorFromA2A function that scans all artifacts for adcp_error. That function is optimized for error detection (scanning all parts for the error key). This function is the general-purpose extractor (last DataPart from first artifact). For failed tasks with a single adcp_error DataPart, both produce equivalent results.
Typical client flow:
Security Considerations
Seller-Controlled Data
All data in.artifacts[].parts[].data and status.message.parts[].data is seller-controlled. The prompt injection, data boundary, and size limit requirements from Transport Error Mapping apply.
Prototype Pollution
Clients MUST NOT merge extracted DataPart payloads into application state viaObject.assign or spread without filtering keys. Validate against the expected task response schema before merging.
FilePart URI Validation
A2A responses may include FileParts (kind: 'file'). Clients MUST validate that uri uses the https scheme, contains no userinfo component, and matches an expected domain allowlist. Reject javascript:, data:, file:, and http: URIs.
Size Limits
Clients SHOULD enforce a maximum DataPart size (e.g., 1MB) before schema validation. Unlike error payloads (capped at 4096 bytes), success payloads can be larger but still need bounds.Intermediary Injection
The last-DataPart convention assumes the artifact is received intact from a single trusted sender. In multi-hop scenarios (buyer → orchestrator → seller), an intermediary could inject additional parts. Clients operating through intermediaries SHOULD validate that the artifact part count matches expectations.Client Library Requirements
Client libraries that implement this spec MUST:- Branch on
status.state. Final states use artifacts; interim states usestatus.message.parts. - Use last DataPart for final states. Skip DataParts with null
.data. - Use first DataPart for interim states.
- Detect and reject wrappers. Single-key
{response: {...}}payloads are bugs. - Fall back gracefully. If artifacts are empty for a final state, check
status.message.parts. - Handle unknown states. Return null, do not throw.
Test Vectors
Machine-readable test vectors are available at/static/test-vectors/a2a-response-extraction.json. Each vector contains:
status: the A2A task statuspath: extraction path (artifact,status_message, ornone)response: the A2A Task or TaskStatusUpdateEventexpected_data: the AdCP data that should be extracted (ornull)expected_error_type: if present, the extraction should throw (e.g.,wrapper_detected)
See Also
- A2A Response Format — canonical response structure for sellers
- Transport Error Mapping — error extraction from MCP and A2A
- MCP Response Extraction — equivalent spec for MCP
- A2A Guide — A2A transport integration