Skip to content

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

Merged
Iwaniukooo11 merged 3 commits into
a2aproject:mainfrom
Linux2010:fix/1330-zero-event-hang
Nov 12, 2026
Merged

Iwaniukooo11 merged 3 commits 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

@Linux2010
Linux2010 requested a review from a team as a code owner October 16, 2026 13:49
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/agent_execution/active_task.py 96.67% 96.69% 🟢 +0.02%
src/a2a/server/request_handlers/default_request_handler_v2.py 88.19% 88.31% 🟢 +0.12%
Total 93.49% 93.50% ⚪️ 0.00%

Generated by coverage-comment.yml

@Linux2010

Copy link
Copy Markdown
Contributor Author

Friendly ping — this is ready for review whenever anyone has bandwidth (both branches just rebased onto latest main). Happy to address any feedback. Thanks!

@Iwaniukooo11

Copy link
Copy Markdown
Member

@Linux2010 Please resolve the conflicts in tests/server/request_handlers/test_default_request_handler_v2.py

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 <[email protected]>
Mapping each review item:

- active_task.py: turn task_created_is_set() into a task_created property.
- active_task.py: shorten the property docstring to one sentence.
- default_request_handler_v2.py: shorten the guard comment to one sentence.
- default_request_handler_v2.py: raise InvalidAgentResponseError instead of
  InternalError, matching how this handler already treats a misbehaving
  agent (it is the type used for task-id mismatch and multiple-Message
  streams); the test asserts the new type.

Signed-off-by: andy <[email protected]>
@Linux2010
Linux2010 force-pushed the fix/1330-zero-event-hang branch from 2079ce4 to 153944d Compare November 12, 2026 01:39
The rebase conflict resolution placed SilentExecutor immediately after
the preceding test without the required module-level blank lines,
which ruff format flags on the merge preview.

Signed-off-by: andy <[email protected]>

@Iwaniukooo11 Iwaniukooo11 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Iwaniukooo11
Iwaniukooo11 merged commit ab7e36f into a2aproject:main Nov 12, 2026
18 checks passed
Linux2010 added a commit to Linux2010/a2a-python that referenced this pull request Nov 12, 2026
Line anchors for the cancel-path CopyFrom, the inline push send, the
get_task wait, and the copy-on-yield caveat had drifted after the
13-commit rebase onto main (which also merged a2aproject#1345/a2aproject#1346 — the very
fixes several anchors point at). All eight anchors re-verified against
current code; claim 5 notes the caller-side guard merged via a2aproject#1345.

Signed-off-by: andy <[email protected]>
@Linux2010
Linux2010 deleted the fix/1330-zero-event-hang branch November 13, 2026 12:15
Linux2010 added a commit to Linux2010/a2a-python that referenced this pull request Nov 13, 2026
Line anchors for the cancel-path CopyFrom, the inline push send, the
get_task wait, and the copy-on-yield caveat had drifted after the
13-commit rebase onto main (which also merged a2aproject#1345/a2aproject#1346 — the very
fixes several anchors point at). All eight anchors re-verified against
current code; claim 5 notes the caller-side guard merged via a2aproject#1345.

Signed-off-by: andy <[email protected]>
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

2 participants