Skip to content

fix: drop trailing assistant message when it is empty after rstrip - #7931

Open
Humphrey (HumphreySun98) wants to merge 1 commit into
microsoft:mainfrom
HumphreySun98:fix/rstrip-empty-assistant-message
Open

Humphrey (HumphreySun98) wants to merge 1 commit into
microsoft:mainfrom
HumphreySun98:fix/rstrip-empty-assistant-message

Conversation

@HumphreySun98

@HumphreySun98 Humphrey (HumphreySun98) commented Jul 8, 2026

Copy link
Copy Markdown

Why are these changes needed?

_rstrip_last_assistant_message (used for Claude models in both the Anthropic
and OpenAI clients) is documented to "Remove the last assistant message if it is
empty", but it only stripped trailing whitespace from the content. A trailing
assistant message that is empty — or that becomes empty after stripping — was
still sent, and the Anthropic API rejects a final assistant message with empty
text content. The method also mutated the caller's message objects in place.

This PR strips the trailing whitespace and, when the content becomes empty,
drops the message instead of sending an empty one. A preceding non-empty
assistant message (intentional prefill) is preserved, so this does not regress
prefill use cases. The input messages are copied rather than mutated.

Note: the issue's suggested fix (drop all trailing assistant messages) would
break legitimate assistant-prefill flows; this change only drops the message
when it is actually empty, matching the method's documented intent.

Related issue number

Closes #7768

Related PRs

Checks

  • I've included any doc changes needed for https://microsoft.github.io/autogen/ (none required for this change).
  • I've added tests (if relevant) corresponding to the changes introduced in this PR.
  • I've made sure all auto checks have passed (ran ruff, mypy, and the relevant pytest locally).

`_rstrip_last_assistant_message` (in both the Anthropic and OpenAI clients,
used for Claude models) was documented to "Remove the last assistant message
if it is empty", but it only stripped trailing whitespace from the content. A
trailing assistant message that is empty (or becomes empty after stripping) was
still sent, and the Anthropic API rejects a final assistant message with empty
text content. The method also mutated the caller's message objects in place.

Strip the trailing whitespace and, when the content becomes empty, drop the
message instead of sending an empty one. A preceding non-empty assistant
message (intentional prefill) is preserved, so this does not regress prefill
use cases. The input messages are now copied rather than mutated.

Fixes microsoft#7768

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HumphreySun98

Copy link
Copy Markdown
Author

Humphrey (@HumphreySun98) please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.

@microsoft-github-policy-service agree [company="{your company}"]

Options:

  • (default - no company specified) I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer.
@microsoft-github-policy-service agree
  • (when company given) I am making Submissions in the course of work for my employer (or my employer has intellectual property rights in my Submissions by contract or applicable law). I have permission from my employer to make Submissions and enter into this Agreement on behalf of my employer. By signing below, the defined term “You” includes me and my employer.
@microsoft-github-policy-service agree company="Microsoft"

Contributor License Agreement

@microsoft-github-policy-service agree

@ErenAta16 ErenAta16 (ErenAta16) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ran both new tests on the branch — 3 passed in each of the Anthropic and OpenAI test files, including the mutation guard. That last part is worth calling out on its own: the old code mutated messages[-1].content on the caller's own message objects in place, which is a separate, subtler bug from the empty-message crash — if the caller reused that message list for logging or a retry, they'd see the stripped version even though they never asked for it to be modified. model_copy(update=...) fixes both issues in one pass.

@HumphreySun98

Copy link
Copy Markdown
Author

Friendly ping Victor Dibia (@victordibia) — this PR and a few sibling bug-fix PRs (#7930, #7933, #7947, #7948) have been open for a few weeks. This one and #7933 have been verified and approved by a community reviewer; as a first-time contributor I'd also need the CI workflows approved to run. Happy to address any feedback whenever you have bandwidth — no rush, and thanks for maintaining the project! 🙏

@HumphreySun98

Copy link
Copy Markdown
Author

Following up on the ping above, now six weeks old, with a more precise ask. cc chetantoshniwal Victor Dibia (@victordibia) Eric Zhu (@ekzhu)

Five small bug-fix PRs from July are ready but cannot progress without a write-access action: the main ruleset only counts approvals from write-access accounts, and no CI workflow has yet run on a first-time contributor's fork here.

Concretely needed per PR: (1) "Approve and run workflows" so the 18 required checks run, (2) an approving review. All are single-commit, rebased on current main, with tests, and "allow edits by maintainers" is on. I will fix any CI failure the same day.

@HumphreySun98

Copy link
Copy Markdown
Author

Closing and immediately reopening to re-create the expired CI workflow runs (explanation follows).

@HumphreySun98

Copy link
Copy Markdown
Author

Correction to my note above, after checking the Actions API: the CI gate on these PRs was not "runs waiting to be approved" — there were no runs at all.

GitHub expires fork pull-request workflow runs that sit unapproved for 30 days, and those expired records are eventually purged. The runs created when these PRs opened in July expired in August, so as of this morning all five carried zero workflow runs. That distinction matters: there was nothing for a maintainer to approve, and a review alone would not have helped either, because the 18 required status checks can never report on a commit that has no runs. mergeStateStatus would have stayed BLOCKED even after an approving review.

I have reopened all five at their existing commits to re-create the runs: #7930, #7931, #7933, #7947, #7948. No commits were changed, nothing was force-pushed, and the existing community approvals are intact. Each now has 7 runs in action_required.

So "Approve and run workflows" is genuinely available on each one now, where before it had nothing to act on. One caveat: that state expires again after 30 days, so the useful window runs to roughly mid-October.

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.

_rstrip_last_assistant_message only strips trailing whitespace from the content — it does not remove the assistant message.

2 participants