Skip to content

fix(server): close the event queues when ActiveTask.start() fails - #1422

Open
cchinchilla-dev wants to merge 1 commit into
a2aproject:mainfrom
cchinchilla-dev:fix/active-task-start-failure-queues
Open

cchinchilla-dev wants to merge 1 commit into
a2aproject:mainfrom
cchinchilla-dev:fix/active-task-start-failure-queues

Conversation

@cchinchilla-dev

Copy link
Copy Markdown
Contributor

Description

Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Follow the CONTRIBUTING Guide.
  • Make your Pull Request title in the https://www.conventionalcommits.org/ specification.
    • Important Prefixes for release-please:
      • fix: which represents bug fixes, and correlates to a SemVer patch.
      • feat: represents a new feature, and correlates to a SemVer minor.
      • feat!:, or fix!:, refactor!:, etc., which represent a breaking change (indicated by the !) and will result in a SemVer major.
  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)
  • Appropriate docs were updated (if necessary)

Fixes #1421 🦕

Problem

Each EventQueueSource starts its dispatcher task when it is created, and ActiveTask creates two of them in __init__. When the setup in start() fails, because the stored task is in a terminal state or is missing, the except block re-raises without closing them:

except Exception:
    logger.debug('ActiveTask[%s]: Setup failed, cleaning up', self._task_id)
    self._is_finished.set()
    if self._reference_count == 0 and self._on_cleanup:
        self._on_cleanup(self)
    raise  # _event_queue_agent and _event_queue_subscribers stay open

A SendMessage to a completed task goes through this path, so every rejected request leaves two _dispatch_loop tasks pending until they are garbage collected and logged as Task was destroyed but it is pending!.

Fix

The except block in start() (src/a2a/server/agent_execution/active_task.py) now closes _event_queue_agent and _event_queue_subscribers with close(immediate=True) before calling _on_cleanup, the same way aclose() closes them. aclose() itself can't be used here because it takes _lock, which start() already holds.

The change only touches the failure path, so a successful start() is not affected.

Test

A test was added to the existing suite at tests/server/agent_execution/test_active_task.py:

  • test_active_task_start_setup_failure_closes_event_queues: after start() fails on a completed task (UnsupportedOperationError) or a missing one (TaskNotFoundError), both dispatcher tasks are done.

Both cases fail on main with the dispatcher tasks still pending and pass with the fix. No existing test was modified or removed. Happy to adapt the test approach based on maintainer feedback.

@cchinchilla-dev
cchinchilla-dev requested a review from a team as a code owner November 3, 2026 17:38
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/server/agent_execution/active_task.py 96.10% 96.67% 🟢 +0.57%
Total 93.42% 93.45% 🟢 +0.03%

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]: ActiveTask.start() leaves the event queue dispatchers pending when setup fails

1 participant