Skip to content

fix: only suppress auto-append when caller explicitly provides a tool result#1541

Closed
Echolonius wants to merge 1 commit into
anthropics:mainfrom
Echolonius:fix/append-messages-infinite-loop
Closed

fix: only suppress auto-append when caller explicitly provides a tool result#1541
Echolonius wants to merge 1 commit into
anthropics:mainfrom
Echolonius:fix/append-messages-infinite-loop

Conversation

@Echolonius
Copy link
Copy Markdown

Summary

Fixes #1536.

Calling runner.append_messages() with a plain user message inside the tool runner loop caused an infinite loop. Any call to append_messages() unconditionally set _messages_modified = True, which the runner interpreted as the caller manually handling the tool result. It would skip its own auto-append of the assistant message and tool result, so the next iteration sent a request with no tool result in history — Claude saw the original question unanswered, re-issued the same tool call, and the loop never terminated.

Root cause

_messages_modified was serving two distinct purposes with no way to tell them apart:

  1. Caller is adding extra context (e.g. append_messages({"role": "user", "content": "be concise"})) — runner should still auto-append the tool result
  2. Caller is manually providing the tool result (e.g. append_messages(assistant_msg, tool_result_msg)) — runner should skip auto-append

Both paths set the flag, so the runner always skipped auto-append regardless of intent.

Fix

_messages_modified is now only set when at least one of the appended messages contains a tool_result content block — the unambiguous signal that the caller is handling the tool result themselves. Appending messages without tool results leaves the flag untouched and the loop continues correctly.

What this affects

  • Sync runner (BetaToolRunner) ✅
  • Async runner (BetaAsyncToolRunner) ✅ — both share the same append_messages() on the base class
  • The working "Modifying tool results" pattern (passing both assistant message + tool result) continues to work unchanged
  • set_messages_params() is unaffected — it never touches the flag

Tests

Added test_append_messages_non_tool_result_does_not_suppress_auto_append — a pure unit test (no API calls required) that directly asserts the flag behaviour for both the plain-message case and the tool-result case.

The existing test_custom_message_handling (marked xfail) is a related but separate issue and remains untouched.

… result

Previously, any call to append_messages() — even adding a plain user
instruction like "be concise" — set _messages_modified=True, which told
the runner to skip its own auto-append of the assistant message and tool
result. The next iteration would send a request with no tool result in
history, Claude would see the original question unanswered, re-issue the
same tool call, and the loop would never terminate.

The fix makes the flag conditional: _messages_modified is only set when
at least one appended message contains a tool_result content block — the
clear signal that the caller is handling the tool result themselves.
Appending other messages (extra instructions, context, etc.) leaves the
flag untouched and the loop continues correctly.

This also aligns with the inconsistency noted in the docs: the "Advanced
usage" example appends a user message without a tool result, while the
"Modifying tool results" example correctly passes both the assistant
message and tool result together. After this fix, both patterns work.

Fixes anthropics#1536.
@Echolonius Echolonius requested a review from a team as a code owner May 14, 2026 05:13
@Echolonius
Copy link
Copy Markdown
Author

Closing in favour of #1538 by @Zelys-DFKH, which was submitted earlier and addresses both failure modes more completely — including the case where a manually-provided tool_result block is sent without the preceding assistant turn, and the removal of the existing xfail test with a proper HTTP fixture update.

I should have checked for open PRs against this issue before opening a duplicate. Apologies for the noise.

@Echolonius Echolonius closed this May 14, 2026
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] append_messages() inside tool runner loop causes infinite loop (advanced usage docs example)

1 participant