Skip to content

Python: [Bug]: A2A INPUT_REQUIRED tasks are auto-answered instead of pausing for the caller #7620

Description

@giles17

Describe the bug

When an A2AAgent participant returns a task in TASK_STATE_INPUT_REQUIRED, the remote agent is asking the caller a question. Agent Framework does not treat this as a pause: A2AAgent surfaces it like a terminal response (_agent.py:631, :820), so the orchestration continues and the next message routed to that participant is silently bound to the waiting task as its answer — without the caller ever being consulted.

The binding is message-agnostic (_agent.py:965-968):

if previous_task_id:
    if task_state == TaskState.TASK_STATE_INPUT_REQUIRED:
        a2a_message.task_id = previous_task_id  # continues the same task

Verified against A2AAgent with a session in INPUT_REQUIRED:

peer message    -> task_id = 'task-42'    reference_task_ids = []
peer, completed -> task_id = ''           reference_task_ids = ['task-42']

So in a group chat, another participant's reply becomes the answer to a confirmation prompt. AgentExecutor holds a persistent AgentSession per executor (_agent_executor.py:174, passed at :435/:491), so task state survives across turns and this is reachable in normal multi-turn use.

Expected behavior

A task awaiting caller input should suspend the workflow and surface a request to the caller. Only caller-supplied input should be bound to the waiting task.

Proposed fix

AgentExecutor already pauses when a response carries user input requests (_agent_executor.py:441-448):

if response.user_input_requests:
    for user_input_request in response.user_input_requests:
        await ctx.request_info(user_input_request, Content, request_id=user_input_request.id)
    return None   # no send_message, no _cache.clear()

user_input_request is a generic flag on Content (_types.py:587) — only the function-approval factories set it today (:1287, :1340), but nothing ties it to approvals.

So A2AAgent should surface INPUT_REQUIRED as a content flagged user_input_request=True. The rest follows: the executor returns early without clearing its cache, the orchestration suspends, the caller gets a standard request_info event, and on reply handle_user_input_response re-runs with the caller's message — which is then correctly bound to the waiting task.

Scope

  • packages/a2a: branch on INPUT_REQUIRED in _updates_from_task / _updates_from_task_update_event (~:757-840), emitting a request-shaped content keyed by task_id.
  • packages/core/_types.py: likely a Content.from_user_input_request(...) factory, since existing producers are approval-shaped. New public API — needs feature-lifecycle review.
  • Round-trip constraint: _prepare_message_for_a2a raises Unknown content type for contents it cannot map to an A2A part, so the caller's reply must serialise to a text part. (Confirmed: the resumed cache is [Message(role="user", contents=[<approval Content>])], whose .text is empty.)
  • Tests for the new shape plus suspend/resume; docs in packages/a2a/AGENTS.md and a HITL sample.

No orchestration changes required

Simulated the post-fix behaviour with a participant that raises a user input request in a two-participant group chat:

request_info events: 1
alpha calls: 1 | beta calls: 0     <- chat halted; beta never invoked
after resume -> alpha calls: 3 | beta calls: 2

The orchestrator only advances in _handle_response and exactly one participant holds should_respond=True per round, so a suspended executor halts the chat implicitly. Handoff and Magentic inherit the same property.

Minor note: _increment_round() fires when the request is sent, so a suspended turn consumes a round.

Risk

This is a behaviour change and arguably breaking. Today a group chat answers a remote agent's question and continues; afterwards it stops and waits for a human. For some users that is the bug being fixed, for others a working flow that begins blocking. Whether it should be unconditional or opt-in is a maintainer decision.

Related

Found while reviewing #7549 (fixes #7456). The auto-answering predates that PR and is independent of it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

pythonUsage: [Issues, PRs], Target: Python

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions