Skip to content

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Jan 1, 2026

fix: use endpoint-scoped task IDs for A2A delegations (fixes #4166)

Summary

Fixes issue #4166 where delegating to a second A2A agent fails with Task <UUID> is in terminal state: completed because the task_id from the first agent was being reused.

The fix introduces endpoint-scoped task ID handling in _delegate_to_a2a:

  • Each A2A endpoint gets its own task_id (not shared across endpoints)
  • Completed task IDs are not persisted for reuse (A2A protocol rejects task IDs in terminal state)
  • Completed task IDs are still tracked in reference_task_ids for history/context purposes
  • Makes a defensive copy of the task ID dictionary to avoid in-place mutation bugs
  • Handles edge case where config value is explicitly None (e.g., from JSON/YAML)

Key changes:

  • wrapper.py: Changed from global task.config["task_id"] to endpoint-scoped lookup; removed persistence of completed task IDs
  • Added new test file with 3 tests covering the fix

Review & Testing Checklist for Human

  • Verify with real A2A agents: The tests use mocks. Please test with actual A2A endpoints to confirm the fix works in practice (configure an agent with 2+ A2A endpoints and delegate sequentially)
  • Confirm completed task IDs should not be reused: The fix intentionally does NOT persist completed task IDs based on the error message in [BUG]Second A2A delegation fails — task is in terminal state (“completed”) #4166 ("terminal state: completed"). Verify this matches expected A2A protocol behavior.
  • Check context_id handling: The fix only scopes task_id by endpoint. Review if context_id (line 589-590) also needs endpoint-scoping to prevent similar issues

Recommended test plan:

  1. Create an agent with two A2A endpoints configured
  2. Run a task that delegates to the first endpoint, then to the second
  3. Verify both delegations complete successfully without "terminal state" errors
  4. Verify that delegating to the same endpoint twice also works (each gets a fresh task_id)

Notes

This fixes issue #4166 where delegating to a second A2A agent fails
because the task_id from the first agent is in 'completed' state.

The fix introduces endpoint-scoped task ID storage in task.config
using a2a_task_ids_by_endpoint dictionary. This ensures that:
- Each A2A endpoint gets its own task_id
- Multi-turn conversations with the same endpoint reuse the task_id
- Sequential delegations to different endpoints use separate task_ids

Added tests to verify:
- Sequential delegation to multiple endpoints uses separate task IDs
- Multi-turn conversations with same endpoint reuse task IDs
- Endpoint-scoped task IDs are properly persisted to task.config

Co-Authored-By: João <joao@crewai.com>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on January 28

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

…rsist completed task IDs

- Make defensive copy of a2a_task_ids_by_endpoint dict to avoid in-place mutation
- Don't persist completed task IDs since A2A protocol rejects terminal state task IDs
- Update task_id_config locally for current loop only, not in shared dict
- Update tests to verify correct behavior:
  - Completed task IDs are NOT persisted for reuse
  - Each new delegation gets a fresh task_id (None)
  - Completed task IDs are still tracked in reference_task_ids

Co-Authored-By: João <joao@crewai.com>
Address Bugbot feedback - handle case where a2a_task_ids_by_endpoint
is explicitly set to None (e.g., from JSON/YAML config) to avoid
TypeError when calling dict() on None.

Co-Authored-By: João <joao@crewai.com>
class MockResponseA:
is_a2a = True
message = "Please help with task A"
a2a_ids = ["http://endpoint-a.com/"]
Copy link

Choose a reason for hiding this comment

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

URL trailing slash mismatch causes tests to fail

All three tests have a URL mismatch that would cause them to fail with ValueError before testing the intended behavior. The mock responses use a2a_ids with trailing slashes (e.g., "http://endpoint-a.com/") while the A2AConfig endpoints are configured without trailing slashes (e.g., "http://endpoint-a.com"). The validation at wrapper.py lines 526-529 checks if agent_id is in the configured agent_ids, and these mismatched URLs would fail the check, raising a ValueError immediately. The tests would never reach the code they're designed to verify.

Additional Locations (2)

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a false positive. The tests pass because they mock _fetch_agent_cards_concurrently and pass agent_cards directly to _delegate_to_a2a. The validation at lines 526-529 checks if agent_id is in the agent_cards dictionary keys, not in the A2AConfig endpoints. Since the tests provide agent_cards with trailing slashes that match the a2a_ids, the validation passes correctly.

The tests are passing in CI (all 3 tests pass across Python 3.10, 3.11, 3.12, and 3.13), confirming this is not an issue.

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

Labels

None yet

1 participant