Skip to content

fix: enforce that the first message is a user message in the sliding window conversation manager#2087

Open
JackYPCOnline wants to merge 1 commit intostrands-agents:mainfrom
JackYPCOnline:fix_silding
Open

fix: enforce that the first message is a user message in the sliding window conversation manager#2087
JackYPCOnline wants to merge 1 commit intostrands-agents:mainfrom
JackYPCOnline:fix_silding

Conversation

@JackYPCOnline
Copy link
Copy Markdown
Contributor

@JackYPCOnline JackYPCOnline commented Apr 7, 2026

Description

SlidingWindowConversationManager can produce a trimmed conversation that starts with an assistant message instead of a user message. Many model providers (including Amazon Bedrock with Nova) require conversations to begin with a user message, causing ValidationException: A conversation must start with a user message.

Root cause: The trim-point validation loop checked for orphaned toolResult and toolUse constraints but never verified that the first remaining message has role == "user".

Secondary bug: A short-circuit evaluation in the toolUse guard let an orphaned toolUse at the last message slip through as a valid trim point:

# Before (broken): trim_index + 1 < len(messages) is False at last message,
# short-circuits the whole sub-expression to False, loop breaks and keeps it.
any("toolUse" ...) and trim_index + 1 < len(messages) and not any("toolResult" ...)

# After (fixed): "no next message" now correctly means "no toolResult" → skip.
any("toolUse" ...) and not (trim_index + 1 < len(messages) and any("toolResult" ...))

This matches the fix already applied in the TypeScript SDK.

###Changes:

Added role != "user" check as the first condition in the trim-point validation loop

Fixed the toolUse short-circuit logic to match the TypeScript SDK behavior

When no valid trim point exists during routine window management (e=None), log a warning and return instead of raising ContextWindowOverflowException — the exception is now only raised when triggered by an actual context overflow error

Why existing tests changed:

Several parametrized test_apply_management cases (5, 7, 8, 9) expected trimmed conversations starting with an assistant message — this was the bug itself. Updated expected outputs to reflect valid user-first trim points, which may keep fewer messages than window_size when no closer valid trim point exists.

test_sliding_window_conversation_manager_with_untrimmable_history_raises_context_window_overflow_exception called apply_management (routine path, e=None) and expected ContextWindowOverflowException. Updated to call reduce_context with an explicit error to test the overflow path. Added a companion test verifying apply_management gracefully no-ops in the same scenario.

test_agent_with_session_and_conversation_manager used window_size=1 with a [user, assistant] conversation. With the fix, there is no valid trim point that preserves a user-first start, so the messages are left unchanged. Updated to use window_size=2 with multiple invocations to properly exercise trimming with session persistence.

Related Issues

#2085

Documentation PR

Type of Change

Bug fix
New feature
Breaking change
Documentation update
Other (please describe):

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

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 Apr 7, 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

github-actions bot commented Apr 7, 2026

Issue: This PR includes unrelated changes beyond the sliding window fix.

The branch diff against main shows changes to 14 files, but only 3 source files + their tests are related to the sliding window bug fix. The unrelated changes include:

Suggestion: Split these into separate PRs — one per logical change. Bundling unrelated changes makes it harder to review, bisect regressions, and revert individual fixes if needed.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Assessment: Comment

The core sliding window fix is solid — the role guard, short-circuit logic correction, and graceful no-op path are all correct and well-tested. The PR description is excellent with clear root cause analysis.

Review Categories
  • Scope: PR bundles unrelated changes (anthropic.py, MCP client, executor) that should be split into separate PRs for cleaner history and easier review
  • Documentation: The reduce_context docstring should be updated to reflect the new e is None → warning behavior
  • Testing: Test cases for tool-heavy conversations now trim very aggressively (down to 1 message); consider adding a "happy path" test showing reasonable trim behavior in typical conversations
  • Code clarity: The toolUse check on user messages is now dead code after the role guard — a brief comment would help future readers

The bug fix logic itself is clean and aligns with the TypeScript SDK. Nice work identifying and fixing both the primary role-check bug and the secondary short-circuit issue.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Assessment: Approve

All feedback from the previous review has been addressed. The fix is correct, well-tested, and cleanly documented.

Changes Since Last Review
  • ✅ Docstring updated to document e is None → warning vs e is not None → raise behavior
  • ✅ Defensive safeguard comment added for the toolUse check after the role guard
  • test_sliding_window_forces_user_message_start now asserts count and content, not just role
  • test_sliding_window_happy_path_preserves_window_size added to demonstrate non-aggressive trimming in typical conversations
  • ✅ Unrelated files confirmed not in the PR scope (GitHub diff shows only 5 relevant files)

@JackYPCOnline JackYPCOnline changed the title fix: force user message in sliding window fix: enforce first message is user message in sliding window conversation manager Apr 7, 2026
@JackYPCOnline JackYPCOnline changed the title fix: enforce first message is user message in sliding window conversation manager fix: enforce first message beiging user message in sliding window conversation manager Apr 7, 2026
@JackYPCOnline JackYPCOnline changed the title fix: enforce first message beiging user message in sliding window conversation manager fix: enforce that the first message is a user message in the sliding window conversation manager Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant