Skip to content

fix: prevent duplicate messages and unpaired tool calls when editing a message - #335

Open
Harrison (iZiTTMarvin) wants to merge 1 commit into
langchain-ai:mainfrom
iZiTTMarvin:fix/edit-message-duplicates
Open

Harrison (iZiTTMarvin) wants to merge 1 commit into
langchain-ai:mainfrom
iZiTTMarvin:fix/edit-message-duplicates

Conversation

@iZiTTMarvin

Copy link
Copy Markdown

Summary

Addresses #204.

Editing a human message had two problems in src/components/thread/messages/human.tsx:

  • Duplicate message in the optimistic UI. optimisticValues started from getMessagesMetadata(message).firstSeenState.values. In @langchain/langgraph-sdk 1.12.4, firstSeenState is the oldest history state that already contains the edited message (dist/react/stream.lgp.js:271, findLast(history, state => messages.includes(messageId))), so appending the new message rendered the old and new text together until the run streamed back.
  • Unpaired tool calls after a stopped run. If a run was cancelled mid tool call, the normal composer pairs the dangling call with ensureToolCallsHaveResponses (index.tsx), but the edit path did not. Forking from the parent checkpoint then sent [..., ai(tool_calls), human], which providers like Anthropic reject (tool_use ids were found without tool_result blocks).

Changes (single file):

  • Build optimistic values from the parent checkpoint's state (looked up in thread.history by checkpoint_id), i.e. exactly what the server forks from. This does not depend on message ids.
  • Pair dangling tool calls with ensureToolCallsHaveResponses, same as a normal submit.
  • Give the edited message a uuidv4() id, like the main composer.
  • If the parent checkpoint is unavailable, show a toast and keep the editor open instead of submitting.

The server-side fork itself (submit(input, { checkpoint: parent })) was already correct, so updateState + submit(null) is not needed.

Repro (deterministic local LangGraph.js graph, no model keys)

  1. Send "weather?" (one tool call), then "hello"; edit "hello". Before: optimistic messages [..., "hello", "edited"]. After: [..., "edited"]. Final server state is unchanged and tool call/result ids stay paired.
  2. Send a message that triggers a slow tool, cancel mid tool call, send "hello", edit it. Before: run fails with tool_use ids were found without tool_result blocks immediately after: call_1 (provider validation mimicked in the graph). After: the run completes.

Verification

  • pnpm format:check and pnpm build: pass.
  • eslint .: 0 errors and the same 18 existing warnings as main (on Windows, run with NODE_OPTIONS set manually because the lint script uses POSIX env syntax).
  • Manually checked against langgraphjs dev with the graph above.

Limitations

  • The provider error was reproduced with a validator that mimics Anthropic, not a real provider call.
  • Not tested against the Python server.
  • The original report doesn't mention cancelled runs, so there may be other triggers for the tool_use error.
  • As before, editing replaces the content with text, so non-text blocks of the original message are not kept.

…a message

Build the edit's optimistic values from the parent checkpoint instead of the state that already contains the edited message, give the new message a uuid like the main composer, pair dangling tool calls as a normal submit does, and show an error instead of submitting when the parent checkpoint is unavailable.
Copilot AI balanced review requested due to automatic review settings October 24, 2026 17:22
@vercel

vercel Bot commented Oct 24, 2026

Copy link
Copy Markdown

Harrison (@iZiTTMarvin) is attempting to deploy a commit to the LangChain Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

This branch has not been deployed

No deployments
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.

2 participants