Skip to content

Fix async compaction using unfiltered messages instead of local copy#1234

Open
MaxwellCalkin wants to merge 1 commit intoanthropics:mainfrom
MaxwellCalkin:fix/async-compaction-uses-unfiltered-messages
Open

Fix async compaction using unfiltered messages instead of local copy#1234
MaxwellCalkin wants to merge 1 commit intoanthropics:mainfrom
MaxwellCalkin:fix/async-compaction-uses-unfiltered-messages

Conversation

@MaxwellCalkin
Copy link

@MaxwellCalkin MaxwellCalkin commented Mar 8, 2026

Summary

  • Bug: BaseAsyncToolRunner._check_and_compact() uses *self._params["messages"] instead of the locally-filtered *messages when constructing the messages list for the compaction API call
  • Impact: The async compaction path sends unfiltered messages to the API, potentially including orphaned tool_use blocks without corresponding tool_result blocks, which causes a 400 error from the API
  • Fix: Replace *self._params["messages"] with *messages on line 472, aligning the async version with the sync version which already correctly uses *messages

Details

In _check_and_compact(), lines 455-469 copy self._params["messages"] to a local messages list and filter out tool_use blocks from the last assistant message (or remove the entire message if all blocks are tool_use). This filtering is necessary because tool_use blocks require corresponding tool_result blocks, and sending them without results causes a 400 API error.

However, line 472 in the async version (BaseAsyncToolRunner) incorrectly discards this filtered copy by spreading from the original self._params["messages"] instead of the local messages.

The sync version (BaseSyncToolRunner._check_and_compact(), line 211) correctly uses *messages.

Test plan

  • Verify that async tool runner compaction works correctly when the last message contains tool_use blocks
  • Existing tests continue to pass

🤖 Generated with Claude Code

AI Disclosure

This PR was authored by Claude Opus 4.6 (Anthropic), an AI agent operated by Maxwell Calkin (@MaxwellCalkin).

…al copy

In BaseAsyncToolRunner._check_and_compact(), the code copies
self._params["messages"] to a local `messages` list and then filters out
tool_use blocks from the last assistant message to avoid a 400 API error
(tool_use blocks require corresponding tool_result blocks). However, when
constructing the final messages list to send to the API, the async version
incorrectly uses `*self._params["messages"]` (the original, unfiltered
list) instead of `*messages` (the filtered local copy).

This bug means the async compaction path always sends unfiltered messages,
potentially including orphaned tool_use blocks, which can cause a 400
error from the API.

The sync version (BaseSyncToolRunner._check_and_compact) correctly uses
`*messages`. This fix aligns the async version with the sync version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@MaxwellCalkin MaxwellCalkin requested a review from a team as a code owner March 8, 2026 22:17
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.

1 participant