Skip to content

fix(session): follow-up message overwrites historical user message text#256

Open
oss-agent-shin wants to merge 1 commit into
BerriAI:mainfrom
oss-agent-shin:fix/followup-msg-wipes-history
Open

fix(session): follow-up message overwrites historical user message text#256
oss-agent-shin wants to merge 1 commit into
BerriAI:mainfrom
oss-agent-shin:fix/followup-msg-wipes-history

Conversation

@oss-agent-shin
Copy link
Copy Markdown
Contributor

Bug

Sending a follow-up message in a session that already has prior conversation history caused the follow-up text to silently replace the first historical user message in the thread instead of appearing as a new entry.

Root causesrc/app/sessions/[sid]/view.tsx, messages useMemo:

The sentUsersthread.messages mapping was purely positional: sentUsers[u] was applied to the u-th user message in the thread, starting from index 0. When a session has existing history, thread.messages already contains past user messages on load, and sentUsers starts empty. The moment the user sends a follow-up:

  • sentUsers = [{text: "follow up"}]
  • thread.messages still has [user1_historical, asst1_historical]
  • The memo mapped sentUsers[0]user1_historical, replacing its text
  • The follow-up never appeared as a new message (the while append loop didn't fire because u === sentUsers.length after the for-loop)

Fix

Capture sentUserBaseRef.current — the number of user messages already in the thread when the very first send happens in a page session. The memo then skips that many historical user messages before it starts applying sentUsers overrides. sentUsers[0] now maps to the first new user message slot, not to a historical one.

The ref is:

  • set synchronously on first send (before setSentUsers), so it's already in place when the memo re-runs after the state update
  • reset to 0 when sessionId changes (same effect as the existing setSentUsers([]) reset)

Test plan

  • Open a session that has prior conversation history (user prompt + assistant response)
  • Type and send a follow-up message
  • Verify the follow-up appears as a new message bubble at the end of the thread
  • Verify the original first user message text is unchanged
  • Verify sending a second follow-up also appends correctly
  • Verify switching sessions and sending works correctly (base resets)

🤖 Generated with Claude Code

When a session has existing history, thread.messages already contains
prior user messages seeded from the /message endpoint. sentUsers starts
empty, so sentUsers[0] (the first new follow-up) was positionally mapped
to the first historical user message — replacing its text in the UI —
instead of being appended as a fresh entry at the end.

Root cause: the sentUsers→thread position mapping counted ALL user messages
starting from index 0, with no awareness of how many pre-existed.

Fix: capture sentUserBaseRef.current = count of user messages already in
the thread at the moment the very first send fires. The messages memo skips
that many historical user messages before applying sentUsers overrides, so
sentUsers[0] always maps to the first new user message slot.

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

greptile-apps Bot commented May 21, 2026

Greptile Summary

This PR fixes a positional off-by-one in the messages useMemo where sentUsers entries were mapped onto thread.messages starting from index 0, silently overwriting the first historical user message when a follow-up was sent in a pre-existing session.

  • A sentUserBaseRef is lazily captured on the very first send, recording how many user messages already exist in thread.messages; the memo skips that many historical entries before it begins applying sentUsers overrides.
  • The ref is correctly reset to 0 alongside setSentUsers([]) in the sessionId change effect, and the synchronous capture-before-setSentUsers ordering ensures the memo sees the right offset on the same render cycle.

Confidence Score: 4/5

Safe to merge; the fix is targeted and well-reasoned, with only a minor missing useCallback dependency worth cleaning up before the next change to that function.

The core mechanism is correct — sentUserBaseRef is set synchronously before setSentUsers, the memo reads it on the same re-render, and the reset path on session switch is complete. The only issue is that sentUsers is read inside handleSend but omitted from its dependency array; this is benign today because draft is always cleared between sends, but it leaves the guard fragile for future edits.

src/app/sessions/[sid]/view.tsx — specifically the handleSend useCallback dependency array.

Important Files Changed

Filename Overview
src/app/sessions/[sid]/view.tsx Introduces sentUserBaseRef to offset the sentUsersthread.messages mapping past historical user messages; logic is sound and correctly reset on session change, with one minor missing dependency in the handleSend useCallback array.

Comments Outside Diff (1)

  1. src/app/sessions/[sid]/view.tsx, line 531 (link)

    P2 sentUsers is read inside handleSend (the sentUsers.length === 0 guard is the entire mechanism of this fix) but is absent from the useCallback dependency array. In practice the callback is recreated after every send because draft is always reset to "", so the stale-closure window is very short and the check behaves correctly today. However, the missing dep means any future change that allows draft to stay unchanged between two sends (e.g. a "Send on click" flow with no text, only attachments already present from a prior cleared state) could cause sentUsers.length to be read as 0 again on the second send, silently re-setting sentUserBaseRef.current and reintroducing the original bug. Adding sentUsers to the deps array makes the guarantee explicit and will also silence any exhaustive-deps lint rule.

Reviews (1): Last reviewed commit: "fix(session): follow-up message overwrit..." | Re-trigger Greptile

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.

1 participant