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
- Lenguaje dominante
- Python
- Estrellas
- 21.5k
- Forks
- 4k
- Merge medio
- 1 d 14 h
- PR fusionados (30 d)
- 37
Descripción
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 #4309, restating because both hooks from point 1 need to handle both shapes.
3. **`TaskState.failed` isn't version-safe.** The proposed fix in #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):
```python
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.
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.