Skip to content

Follow-up to #4309: fix for failed A2A tasks needs both _handle_a2a_response/_v2, _compat.TS_FAILED, and error_code — still reproducible in 2.6.2 #6708

Description

@ferponse

Follow-up to #4309 ("Failed A2A task errors leak into conversation history as regular content"), closed for lack of follow-up rather than a fix. Filing separately since we hit this independently on a newer version and found a few things the original report and its proposed workaround don't cover — happy to have this folded into #4309 instead if that's preferred.

Still present in google-adk==2.6.2 (Python 3.12). convert_a2a_task_to_event() (google/adk/a2a/converters/event_converter.py) never reads a2a_task.status.state; it only extracts message content and returns a plain Event. Same root cause as reported for 1.1.0 → 1.24.1.

This is a round-trip asymmetry, not just a missing check. convert_event_to_a2a_events() (used when ADK is the A2A server) already does the reverse correctly: if event.error_code is set, _create_error_status_event() builds a TaskStatus(state=TASK_STATE_FAILED, ...). So ADK already models "failed task" as a first-class A2A state on the way out — it just never reads that same state back in on the way in. Two ADK instances talking A2A to each other already lose this, not only ADK-vs-third-party.

Three things worth folding into whatever fix lands:

  1. Which hook actually fires. RemoteA2aAgent dispatches each response to either _handle_a2a_response or _handle_a2a_response_v2 depending on whether the task carries ADK's own integration-extension marker. A third-party (non-ADK) A2A server never sets that marker, so every response goes through _handle_a2a_response_handle_a2a_response_v2 never fires against it at all. A workaround/fix that only patches _v2 silently does nothing in that setup.
  2. Streaming vs non-streaming shape. With streaming=True the terminal state arrives as an A2ATaskStatusUpdateEvent (update.status.state), not on the Task itself (task.status.state) — already flagged in Failed A2A task errors leak into conversation history as regular content #4309, restating because both hooks from point 1 need to handle both shapes.
  3. TaskState.failed isn't version-safe. The proposed fix in Failed A2A task errors leak into conversation history as regular content #4309 compares against TaskState.failed, which only exists on the pydantic-enum shape of a2a-sdk (0.3.x). On the protobuf shape (1.x, e.g. a2a-sdk==1.1.2, bundled with google-adk==2.6.2), TaskState has no .failed attribute at all — the value is TaskState.Value("TASK_STATE_FAILED"). ADK already has a shim for exactly this split: google.adk.a2a._compat.TS_FAILED. Any fix should compare against that instead of a hardcoded enum member.

Also: the proposed fix sets error_message but not error_code. That stops the leak into history, but callers that want to tell "remote task failed" apart from other RemoteA2aAgent error paths generally key off error_code (which exists on Event for exactly that). Worth setting a stable code (e.g. "A2A_TASK_FAILED") alongside the message.

Sketch (untested against internals of every version, but shows the shape):

from google.adk.a2a import _compat

if task and task.status and task.status.state == _compat.TS_FAILED:
    event = Event(
        author=self.name,
        error_code="A2A_TASK_FAILED",
        error_message=extracted_text_from(event) or "Remote agent task failed",
        invocation_id=ctx.invocation_id,
        branch=ctx.branch,
    )

applied in both _handle_a2a_response and _handle_a2a_response_v2, checking both the Task and TaskStatusUpdateEvent shapes.

Environment: google-adk 2.6.2, a2a-sdk 1.1.2, Python 3.12.

Happy to close as a duplicate of #4309 if you'd rather track everything there.

Metadata

Metadata

Labels

a2a[Component] This issue is related a2a support inside ADK.request clarification[Status] The maintainer need clarification or more information from the author

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions