Skip to content

fix: keep concurrent tool results in request order#2340

Merged
opieter-aws merged 1 commit into
strands-agents:mainfrom
he-yufeng:fix/concurrent-tool-result-order-2112
May 29, 2026
Merged

fix: keep concurrent tool results in request order#2340
opieter-aws merged 1 commit into
strands-agents:mainfrom
he-yufeng:fix/concurrent-tool-result-order-2112

Conversation

@he-yufeng
Copy link
Copy Markdown
Contributor

Motivation

ConcurrentToolExecutor currently passes the same tool_results list into every running task. Each task appends when it finishes, so the follow-up toolResult message is ordered by scheduler completion instead of by the model's original toolUse order.

That makes prompts unstable for parallel tool calls: a fast second tool can appear before a slow first tool, breaking prompt-cache and replay determinism.

Closes #2112.

Changes

  • Give each concurrent task its own result list.
  • Merge those per-task results back into the shared tool_results list in tool_uses order after all tasks finish.
  • Add a regression test where the first tool intentionally finishes after the second tool, but tool_results still follows request order.

Testing

  • .\.venv\Scripts\python.exe -m pytest tests\strands\tools\executors\test_concurrent.py -q
  • .\.venv\Scripts\python.exe -m ruff check src\strands\tools\executors\concurrent.py tests\strands\tools\executors\test_concurrent.py
  • .\.venv\Scripts\python.exe -m ruff format --check src\strands\tools\executors\concurrent.py tests\strands\tools\executors\test_concurrent.py
  • .\.venv\Scripts\python.exe -m mypy src\strands\tools\executors\concurrent.py
  • .\.venv\Scripts\python.exe -m py_compile src\strands\tools\executors\concurrent.py tests\strands\tools\executors\test_concurrent.py
  • git diff --check

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Approve

Clean, minimal fix that correctly addresses the ordering issue described in #2112. The approach of per-task result lists merged in tool_uses order is straightforward and maintains the existing contract.

Review Notes
  • Correctness: The fix properly isolates results per-task and merges in deterministic order after all tasks complete. The error path (exception raised in the while loop) skips the merge, which produces empty tool_results on failure—consistent with the existing test expectation (test_concurrent_executor_reraises_exceptions asserts tool_results == []).
  • Testing: The regression test is well-designed with a clear timing differential that validates ordering. Existing tests continue to pass with the new semantics.

Nice targeted fix! 👍

@opieter-aws
Copy link
Copy Markdown
Contributor

@he-yufeng the implementation looks correct, thanks! Can you please rebase and fix failing CI?

@he-yufeng he-yufeng force-pushed the fix/concurrent-tool-result-order-2112 branch from 4d941a7 to 27dda6d Compare May 29, 2026 07:42
@github-actions github-actions Bot removed the size/s label May 29, 2026
@he-yufeng
Copy link
Copy Markdown
Contributor Author

Rebased this branch onto current main and force-pushed 27dda6d.

Validation after rebase:

  • uvx hatch test -n 0 --basetemp .tmp/pytest tests/strands/tools/executors/test_concurrent.py tests/strands/tools/test_caller.py::test_tool_caller_raises_reference_error_after_agent_collected
  • uvx griffe check --search strands-py/src --format github strands --against upstream/main
  • uvx ruff format --check strands-py/src/strands/tools/executors/concurrent.py strands-py/tests/strands/tools/executors/test_concurrent.py
  • uvx ruff check strands-py/src/strands/tools/executors/concurrent.py strands-py/tests/strands/tools/executors/test_concurrent.py
  • git diff --check

The earlier check-api warning was against the stale local main ref; it passes against current upstream/main after the rebase. Full format-check still reports an unrelated pre-existing formatting difference in tests/strands/models/test_gemini.py, so I did not include unrelated formatting churn in this PR.

@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Approve

Clean, minimal fix that correctly addresses the ordering issue described in #2112. The approach of per-task result lists merged in tool_uses order is straightforward and maintains the existing contract.

Review Notes
  • Correctness: The fix properly isolates results per-task and merges in deterministic order after all tasks complete. The error path (exception raised in the while loop) skips the merge, which produces empty tool_results on failure—consistent with the existing test expectation (test_concurrent_executor_reraises_exceptions asserts tool_results == []).
  • Testing: The regression test is well-designed with a clear timing differential that validates ordering. Existing tests continue to pass with the new semantics.

Nice targeted fix.

@yonib05 yonib05 added bug Something isn't working python Pull requests that update python code labels May 29, 2026
@opieter-aws opieter-aws merged commit a3e58d5 into strands-agents:main May 29, 2026
23 of 24 checks passed
@chaynabors
Copy link
Copy Markdown
Member

chaynabors commented May 29, 2026

Hey @he-yufeng, sorry for the trouble, was this contribution made under the terms of Apache 2.0? Asking because we found another license in the codebase that has since been removed, and would otherwise have to revert this.

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

Labels

bug Something isn't working python Pull requests that update python code size/s

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] ConcurrentToolExecutor collects tool_results in completion order, breaking prompt-cache stability

4 participants