Skip to content

fix(server): warn when queue_manager is ignored in DefaultRequestHandlerV2 - #1153

Open
NishchayMahor wants to merge 2 commits into
a2aproject:mainfrom
NishchayMahor:fix/v2-warn-ignored-queue-manager
Open

fix(server): warn when queue_manager is ignored in DefaultRequestHandlerV2#1153
NishchayMahor wants to merge 2 commits into
a2aproject:mainfrom
NishchayMahor:fix/v2-warn-ignored-queue-manager

Conversation

@NishchayMahor

Copy link
Copy Markdown

Summary

Fixes #1135.

DefaultRequestHandlerV2.__init__ accepts a queue_manager argument (kept for signature compatibility with DefaultRequestHandler) but never stores or uses it. As @rohityan confirmed on the issue, v2 delegates event streaming to an in-memory ActiveTaskRegistry, so any custom or distributed QueueManager — e.g. a Redis-backed one used for multi-replica stream reconnection — is silently dropped. After upgrading, multi-replica streaming breaks with no error or warning.

Fix

Emit a logger.warning at construction when a non-None queue_manager is passed, explaining that it is ignored in v2 and pointing at the documented workarounds (switch to LegacyRequestHandler, or route /tasks/{id}:subscribe to the replica holding the ActiveTask). This turns a silent misconfiguration into a visible one without changing v2's architecture. Also corrected the now-inaccurate inline comment on the parameter.

Evidence

  • The parameter is dropped: default_request_handler_v2.py __init__ binds agent_executor/task_store/etc. but has no self._queue_manager = queue_manager (contrast default_request_handler.py:123, which does store it and uses it at lines 209/313/503/611).
  • v2 uses the registry instead: self._active_task_registry = ActiveTaskRegistry(...).

Testing

Added two tests to tests/server/request_handlers/test_default_request_handler_v2.py:

  • test_init_warns_when_queue_manager_passed — asserts a WARNING mentioning queue_manager is logged (fails on main, passes with the fix).
  • test_init_no_warning_without_queue_manager — asserts no such warning in the default case.
uv run pytest tests/server/request_handlers/            # 195 passed

ruff check, ruff format --check, and ty check all clean on the changed files.

This change was developed with AI assistance; I verified the root cause and behavior against the code paths cited above, reviewed every line, and ran the tests.

…lerV2

DefaultRequestHandlerV2 accepts a queue_manager for signature compatibility
but never stores or uses it — v2 delegates event streaming to an in-memory
ActiveTaskRegistry, so a custom or distributed QueueManager (e.g. Redis for
multi-replica reconnection) is silently dropped. Emit a warning at
construction so the misconfiguration is visible, pointing at the
LegacyRequestHandler / task-affinity workarounds.

Fixes a2aproject#1135
@NishchayMahor
NishchayMahor requested a review from a team as a code owner July 27, 2026 21:26
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/events/event_queue_v2.py 91.79% 91.28% 🔴 -0.51%
src/a2a/server/request_handlers/default_request_handler_v2.py 94.17% 94.25% 🟢 +0.08%
src/a2a/utils/telemetry.py 91.47% 90.70% 🔴 -0.78%
Total 93.00% 92.98% 🔴 -0.02%

Generated by coverage-comment.yml

@chopmob-cloud

Copy link
Copy Markdown

Confirmed the root cause on current main: DefaultRequestHandlerV2 still accepts queue_manager in its signature (84) but never stores or uses it, since V2 drives streaming through ActiveTaskRegistry (108). A caller passing a Redis-backed QueueManager gets it dropped with no signal, which is the exact failure in the issue. Warning on a non-None value is a reasonable stopgap given that actually honoring a distributed manager in V2 is an architectural change, and it does satisfy the issue's stated bar (either the parameter works, or passing one warns).

One suggestion to make the warning land where it matters. logger.warning surfaces in server logs, but the point of the issue is that this is easy to miss, especially across replicas. warnings.warn(..., DeprecationWarning, stacklevel=2) would put the signal at the caller's construction site, make it catchable and filterable in test suites, and read as a deprecation of the parameter rather than a runtime log line. Emitting both (the DeprecationWarning plus the existing log) would cover interactive and headless callers. Since the parameter is being kept only for backward compatibility, stating that deprecation in the docstring would also carry it into the generated API docs.

Tests and CI are green. This is a clean interim fix; the warnings.warn form would match the issue's raises-or-warns framing a little more faithfully.

Adds warnings.warn(..., DeprecationWarning, stacklevel=2) alongside the
existing logger.warning so the signal lands at the caller's construction
site and is catchable in test suites. Documents the ignored parameter on
the class docstring.

Matches the existing convention in a2a/server/events/event_queue.py.
@NishchayMahor

Copy link
Copy Markdown
Author

Thanks for actually going and checking this against main rather than taking the description at face value — the line references are right, and confirming that queue_manager appears only in the signature while __init__ builds the ActiveTaskRegistry is the part that matters.

On warnings.warn(..., DeprecationWarning, stacklevel=2): agreed, and more so than I expected, because the codebase already does exactly this. EventQueue.__new__ in server/events/event_queue.py uses the same three-argument form for the same shape of problem — a parameter/entry point kept only for backward compatibility. So I've matched that rather than inventing a house style, and I've kept the logger.warning too, for the reason you gave: DeprecationWarning is ignored by default outside __main__, so a headless server would otherwise see nothing at all. stacklevel=2 is asserted in the test via record[0].filename, so it will fail if the attribution ever regresses.

The docstring suggestion I've taken but moved. DefaultRequestHandlerV2.__init__ has no docstring at all — unlike DefaultRequestHandler, which carries a full Args: block — and adding an Args: section documenting one parameter out of nine seemed worse than the alternative, so the note is on the class docstring, which still lands in the generated docs.

One thing that came out of this that's worth recording: the repo's own integration harness hits this. tests/integration/test_scenarios.py, create_handler does

queue_manager = queue_manager or InMemoryQueueManager()

and then passes it positionally to DefaultRequestHandlerV2, where it's dropped. So this isn't only a hypothetical about someone wiring up Redis — there's an in-tree caller already relying on a parameter that does nothing.

Numbers on the updated branch: 61 passed in test_default_request_handler_v2.py, 1775 passed / 90 skipped / 3 xfailed / 1 xpassed across tests/, ruff clean. With the source change stashed, the new test fails with DID NOT WARN. filterwarnings in pyproject.toml has no error entry, so the new warning doesn't turn any existing run red — the integration scenarios still pass, they just emit it now.

What I haven't done is make V2 honor a real QueueManager. That's the architectural change you alluded to, and it needs a maintainer's call on whether V2 is ever meant to support distributed streaming or whether the parameter should eventually come off the signature entirely. This stays a warn-only stopgap.

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]: queue_manager passed to DefaultRequestHandler is silently ignored since v2

2 participants