Type of issue
issue / bug
Language
N/A
Description
Summary
When implementing the branching chat pattern with @langchain/vue, following the documented handleEdit implementation
causes all branch switchers to disappear after editing a message. The root cause is an ID collision between the edited message and the original in getMessagesMetadataMap's findLast lookup.
Environment
@langchain/vue (latest)
@langchain/langgraph-sdk (latest)
fetchStateHistory: { limit: N } enabled
Steps to Reproduce
- Start a new conversation and send a message β AI responds
- Regenerate the AI response β branch switcher appears (1/2 Β· 2/2)
- Edit the original human message following the documented pattern:
// As documented
stream.submit(
{ messages: [{ ...msg, content: newText }] }, // keeps original id
{ checkpoint }
);
- Observe: after the edit completes, all branch switchers disappear β including the one created by the regen in step 2
Root Cause
The documented pattern spreads the original message object, preserving its id:
{ messages: [{ ...originalMsg, content: newText }] }
getMessagesMetadataMap in branching.js uses findLast to locate the oldest history state containing each message id:
// branching.js
const firstSeenState = findLast(options.history ?? [], (state) =>
state.values != null &&
options.getMessages(state.values)
.map((m, idx) => m.id ?? idx)
.includes(messageId)
);
const checkpointId = firstSeenState?.checkpoint?.checkpoint_id;
let branch = checkpointId != null
? options.branchContext.branchByCheckpoint[checkpointId]
: void 0;
Since the edited message shares its id with the original, findLast returns the oldest state containing that id which is a checkpoint on the original branch, not the edit branch. That
checkpoint is absent from the edit branch's branchByCheckpoint map, so branch resolves to undefined β branchOptions is never set β no branch switcher is rendered.
Workaround
Submit the edited message without the original id so that LangGraph generates a new one, making findLast correctly resolve to the edit branch's checkpoint:
stream.submit(
{ messages: [{ role: "human", content: newText }] }, // β no id
{ checkpoint }
);
Expected Behavior
The documented pattern { ...msg, content: newText } should either:
- work correctly (fix getMessagesMetadataMap to resolve firstSeenState relative to the current branch context rather than the global history), or
- be updated in the documentation to omit the message id
Additional Notes
A related issue: when submitting a normal new message (not an edit) from a non-default branch without passing an explicit checkpoint, submitDirect resets this.#branch = ""
(orchestrator.js:634). After stream completion getBranchView("") selects the newest fork branch rather than the one the user was viewing, causing the new response to disappear. Fix: pass
history.value.at(-1)?.checkpoint explicitly on every submit to preserve the active branch.
Type of issue
issue / bug
Language
N/A
Description
Summary
When implementing the branching chat pattern with
@langchain/vue, following the documentedhandleEditimplementationcauses all branch switchers to disappear after editing a message. The root cause is an ID collision between the edited message and the original in
getMessagesMetadataMap'sfindLastlookup.Environment
@langchain/vue(latest)@langchain/langgraph-sdk(latest)fetchStateHistory: { limit: N }enabledSteps to Reproduce
Root Cause
The documented pattern spreads the original message object, preserving its id:
getMessagesMetadataMapin branching.js uses findLast to locate the oldest history state containing each message id:Since the edited message shares its id with the original, findLast returns the oldest state containing that id which is a checkpoint on the original branch, not the edit branch. That
checkpoint is absent from the edit branch's branchByCheckpoint map, so branch resolves to undefined β branchOptions is never set β no branch switcher is rendered.
Workaround
Submit the edited message without the original id so that LangGraph generates a new one, making findLast correctly resolve to the edit branch's checkpoint:
Expected Behavior
The documented pattern { ...msg, content: newText } should either:
Additional Notes
A related issue: when submitting a normal new message (not an edit) from a non-default branch without passing an explicit checkpoint, submitDirect resets this.#branch = ""
(orchestrator.js:634). After stream completion getBranchView("") selects the newest fork branch rather than the one the user was viewing, causing the new response to disappear. Fix: pass
history.value.at(-1)?.checkpoint explicitly on every submit to preserve the active branch.