Skip to content

fix(server): fail fast when an executor produces no events - #1345

Open
Linux2010 wants to merge 1 commit into
a2aproject:mainfrom
Linux2010:fix/1330-zero-event-hang
Open

Linux2010 wants to merge 1 commit into
a2aproject:mainfrom
Linux2010:fix/1330-zero-event-hang

Conversation

@Linux2010

Copy link
Copy Markdown
Contributor

Description

With an AgentExecutor that returns normally without enqueueing any event, message/send hung forever on both the request coroutine and the ActiveTask producer/consumer tasks: result was None, and ActiveTask.get_task() awaited _task_created with no timeout — but _task_created is only set when a task event is processed, the task pre-existed at start(), or the producer failed. A silent success path sets none of them. Root-cause analysis and a runtime reproduction are in #1330.

Per the approach endorsed on the issue, this fail-fasts with InternalError when the subscribe loop finishes without a task event:

  • New ActiveTask.task_created_is_set() accessor (public wrapper over the internal _task_created event) so the handler doesn't reach into private state.
  • DefaultRequestHandlerV2.on_message_send: raise InternalError when result is None and task_created_is_set() is False, before awaiting get_task().

Follow-up (not in this PR): the legacy handler contains an equivalent raise InternalError guard (default_request_handler.py:456) that is empirically unreachable in this scenario — the flow blocks earlier in consume_and_break_on_interrupt. Making that reachable is a separate piece of work.

  • Follow the CONTRIBUTING Guide
  • PR title follows Conventional Commits (fix: → SemVer patch)
  • Tests and linter pass
  • Appropriate docs were updated (accessor docstring states the guarantee)

Fixes #1330 🦕

Validation

  • Claim: a zero-event executor now fails fast with InternalError instead of hanging; an event-producing executor is unaffected.
  • Exact head: 1ed3680
  • BEFORE: message/send never returns (3s+ timeout in repro, issue [Bug]: message/send hangs forever when the AgentExecutor returns without producing events #1330). AFTER: raises InternalError('Agent execution produced no events') immediately.
  • Tests: new test_on_message_send_raises_when_agent_produces_no_events; handler + agent_execution suites green.
  • Environment: Python 3.10.20, macOS.
  • Limits / not tested: the optional timeout/completion-signaling complement discussed on the issue — the is_set() guard fully covers the unreachable-wait case; happy to add on request.

Prepared with AI assistance (WorkBuddy); reviewed and verified by Linux2010

With a zero-event executor, message/send hung forever: the result was
None and ActiveTask.get_task() awaited _task_created with no timeout,
but _task_created is only set when a task event is processed, the task
pre-existed at start(), or the producer failed — none of which happens
on a silent success path (issue a2aproject#1330).

Per the approach endorsed on the issue, fail fast with InternalError
when the subscribe loop finished without a task event, guarded by the
new ActiveTask.task_created_is_set() accessor. The legacy handler's
unreachable 'raise InternalError' guard is unchanged; making it
reachable is a separate follow-up.

Signed-off-by: andy <linux2011@qq.com>
@Linux2010
Linux2010 requested a review from a team as a code owner October 16, 2026 13:49
@github-actions

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/agent_execution/active_task.py 95.40% 95.42% 🟢 +0.02%
src/a2a/server/cluster/database_event_stream.py 96.94% 92.86% 🔴 -4.08%
src/a2a/server/request_handlers/default_request_handler_v2.py 87.46% 86.74% 🔴 -0.72%
Total 93.17% 93.11% 🔴 -0.07%

Generated by coverage-comment.yml

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: message/send hangs forever when the AgentExecutor returns without producing events

1 participant