Skip to content

prebuilt: add filter_orphaned_reasoning_messages pre_model_hook#7229

Open
Alan Andrade (alan-andrade) wants to merge 1 commit intolangchain-ai:mainfrom
alan-andrade:aa/filter-orphaned-reasoning-messages
Open

prebuilt: add filter_orphaned_reasoning_messages pre_model_hook#7229
Alan Andrade (alan-andrade) wants to merge 1 commit intolangchain-ai:mainfrom
alan-andrade:aa/filter-orphaned-reasoning-messages

Conversation

@alan-andrade
Copy link
Copy Markdown

Why

When a client aborts a streaming response mid-flight, LangGraph may persist an AIMessage whose content consists exclusively of type="reasoning" blocks with no accompanying text or tool-call output. On the next conversation turn, replaying that orphaned message causes a provider error:

BadRequestError: Item 'rs_...' of type 'reasoning' was provided without its required following item.

This is a gap in the existing _validate_chat_history logic, which only checks for orphaned tool calls, not orphaned reasoning blocks.

What

Adds a public filter_orphaned_reasoning_messages function to langgraph.prebuilt that is designed to be passed directly as a pre_model_hook to create_react_agent:

from langgraph.prebuilt import create_react_agent, filter_orphaned_reasoning_messages

agent = create_react_agent(
    model,
    tools,
    pre_model_hook=filter_orphaned_reasoning_messages,
)

The function:

  • Scans state["messages"] for AIMessages whose content is a non-empty list of only type="reasoning" blocks and that carry no tool_calls
  • Returns {"messages": [RemoveMessage(id=...), ...]} to strip them before the model call
  • Returns None (no-op) when no orphaned messages are present
  • Skips messages without an id rather than raising

15 unit tests cover the full edge-case surface: empty content, mixed reasoning+text (complete turns, not orphaned), messages with tool_calls, multiple orphans, idempotency, and the no-id edge case.

Notes

This pattern was first implemented in production at dbt Labs to handle stream-abort scenarios with the OpenAI Responses API (o3, o4-mini). It sits alongside the existing _validate_chat_history orphaned-tool-call check and is non-overlapping with it by construction — _is_reasoning_only returns False for any message carrying tool_calls.

Drafted by Claude Sonnet 4.6 under the direction of Alan Andrade (@alan-andrade)

Add a public `filter_orphaned_reasoning_messages` function usable as a
`pre_model_hook` in `create_react_agent`.  When a client aborts a
streaming response mid-flight, LangGraph may persist an AIMessage whose
content is exclusively reasoning blocks with no following text or
tool-call output.  Replaying that orphaned message on the next turn
causes a provider error (e.g. OpenAI Responses API:
"Item 'rs_...' of type 'reasoning' was provided without its required
following item.").

The function scans `state["messages"]` for such orphaned messages and
returns `RemoveMessage` entries to delete them before the model is
called.  If no orphaned messages are found it returns `None`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

This PR has been automatically closed because it does not link to an approved issue.

All external contributions must reference an approved issue or discussion. Please:

  1. Find or open an issue describing the change
  2. Wait for a maintainer to approve and assign you
  3. Add Fixes #<issue_number>, Closes #<issue_number>, or Resolves #<issue_number> to your PR description and the PR will be reopened automatically

Maintainers: reopen this PR or remove the missing-issue-link label to bypass this check.

@github-actions github-actions Bot closed this Mar 24, 2026
@mdrxy Mason Daugherty (mdrxy) added bypass-issue-check Maintainer override: skip issue-link enforcement and removed missing-issue-link labels Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bypass-issue-check Maintainer override: skip issue-link enforcement external

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants