Skip to content

Conversation

@daniel-lxs
Copy link
Member

@daniel-lxs daniel-lxs commented Jan 28, 2026

Problem

When multiple tools are called in parallel (e.g., update_todo_list + new_task), tool_results could appear in API history BEFORE their corresponding tool_use blocks, causing Anthropic API 400 errors:

"unexpected `tool_use_id` found in `tool_result` blocks. Each `tool_result` block must have a corresponding `tool_use` block in the previous message."

Example of malformed history:

[user msg] → [user msg with tool_results] → [assistant msg with tool_uses] ❌

Should be:

[user msg] → [assistant msg with tool_uses] → [user msg with tool_results] ✓

Root Cause

Tools executed during streaming via presentAssistantMessage() BEFORE the assistant message was saved to history. The sequence was:

  1. Stream starts, tool_use chunks arrive
  2. Each tool_use is finalized (marked non-partial)
  3. presentAssistantMessage immediately executes completed tools
  4. update_todo_list executes → pushes tool_result to userMessageContent
  5. new_task executes → calls flushPendingToolResultsToHistory()
  6. User message with tool_results is saved BEFORE assistant message exists in history
  7. Stream completes, assistant message finally saved at Task.ts:3475

Solution

Added assistantMessageSavedToHistory coordination flag to ensure correct message ordering:

  1. Flag is reset at the start of each API request (Task.ts:2724)
  2. Flag is set to true after assistant message is saved to history (Task.ts:3506)
  3. flushPendingToolResultsToHistory() waits for the flag before flushing (Task.ts:1074-1110)
  4. This ensures: [assistant with tool_uses] → [user with tool_results]

Also removed the "CRITICAL: This tool MUST be called alone" restriction from new_task tool description to enable parallel tool calling.

Changes

  • src/core/task/Task.ts: Added assistantMessageSavedToHistory flag and waiting logic in flushPendingToolResultsToHistory()
  • src/core/prompts/tools/native-tools/new_task.ts: Removed restriction preventing parallel tool calling
  • src/core/task/tests/flushPendingToolResultsToHistory.spec.ts: Added tests for waiting behavior

Testing

All 8 tests pass in the new test file, including tests verifying:

  • ✓ Skips waiting when flag is already true
  • ✓ Waits via pWaitFor when flag is false
  • ✓ Aborts without flushing when task is aborted during wait

All 5228 tests pass across the entire codebase.

Related

This fix enables parallel tool calling (when the multipleNativeToolCalls experiment is enabled) by ensuring the assistant message is always saved to history before tool_results are flushed, preventing the time-travel bug.


Important

Fixes a bug in Task.ts by adding a coordination flag to ensure tool results are saved after assistant messages, preventing API errors.

  • Behavior:
    • Added assistantMessageSavedToHistory flag in Task.ts to ensure tool results are saved after assistant messages.
    • flushPendingToolResultsToHistory() now waits for the flag before proceeding.
    • Removed restriction on new_task tool for parallel execution.
  • Testing:
    • Added tests in flushPendingToolResultsToHistory.spec.ts to verify waiting behavior and flag handling.
    • Tests cover scenarios where the flag is true, false, and task is aborted.
  • Misc:
    • Updated logic in Task.ts to reset and set the flag at appropriate points in the task lifecycle.

This description was created by Ellipsis for bf7a662. You can customize this summary. It will automatically update as commits are pushed.

@daniel-lxs daniel-lxs requested review from cte, jr and mrubens as code owners January 28, 2026 16:20
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jan 28, 2026
@roomote
Copy link
Contributor

roomote bot commented Jan 28, 2026

Rooviewer Clock   See task on Roo Cloud

Review complete. No issues found.

The implementation correctly addresses the time-travel bug in parallel tool calling by:

  1. Adding an assistantMessageSavedToHistory coordination flag to ensure proper message ordering
  2. Making flushPendingToolResultsToHistory() wait for the assistant message before flushing tool results
  3. Properly handling abort scenarios during the wait
  4. Adequate test coverage for the new waiting behavior
Previous reviews

Mention @roomote in a comment to request specific changes to this pull request or fix all unresolved issues.

## Problem
When multiple tools are called in parallel (e.g., update_todo_list + new_task),
tool_results could appear in API history BEFORE their corresponding tool_use
blocks, causing Anthropic API 400 errors:
"unexpected `tool_use_id` found in `tool_result` blocks"

## Root Cause
Tools executed during streaming via presentAssistantMessage() BEFORE the
assistant message was saved to history. When new_task triggered delegation
and called flushPendingToolResultsToHistory(), it saved a user message
(with tool_results) before the assistant message (with tool_uses) existed.

## Solution
Added assistantMessageSavedToHistory coordination flag to ensure correct
message ordering:

1. Flag is reset at the start of each API request
2. Flag is set to true after assistant message is saved to history
3. flushPendingToolResultsToHistory() waits for the flag before flushing
4. This ensures: [assistant with tool_uses] → [user with tool_results]

Also removed the "CRITICAL: This tool MUST be called alone" restriction
from new_task tool description to enable parallel tool calling.

## Changes
- src/core/task/Task.ts: Added assistantMessageSavedToHistory flag and
  waiting logic in flushPendingToolResultsToHistory()
- src/core/prompts/tools/native-tools/new_task.ts: Removed restriction
  preventing parallel tool calling
- src/core/task/__tests__/flushPendingToolResultsToHistory.spec.ts: Added
  tests for waiting behavior

## Testing
All 8 tests pass, including new tests verifying:
- Skips waiting when flag is already true
- Waits via pWaitFor when flag is false
- Aborts without flushing when task is aborted during wait
@daniel-lxs daniel-lxs force-pushed the fix/parallel-tool-calling-time-travel branch from b90fe8d to bf7a662 Compare January 28, 2026 16:48
@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Jan 28, 2026
@mrubens mrubens merged commit f5004ac into main Jan 28, 2026
10 checks passed
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Jan 28, 2026
@github-project-automation github-project-automation bot moved this from Triage to Done in Roo Code Roadmap Jan 28, 2026
@mrubens mrubens deleted the fix/parallel-tool-calling-time-travel branch January 28, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants