Skip to content

fix(chat): keep pending user message visible until its echo arrives#863

Closed
bourgois wants to merge 1 commit into
siteboon:mainfrom
bourgois:fix/pending-message-race
Closed

fix(chat): keep pending user message visible until its echo arrives#863
bourgois wants to merge 1 commit into
siteboon:mainfrom
bourgois:fix/pending-message-race

Conversation

@bourgois

@bourgois bourgois commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Small, self-contained fix extracted from #855 at the maintainer's request.

Issue

The optimistic "pending" user message (shown immediately after you hit send, before the backend echoes it) is only rendered while the message store is empty:

if (pendingUserMessage && all.length === 0) {
  return [pendingUserMessage];
}

On an existing session, the first realtime event after sending can be a stream_delta (assistant content) that arrives before the echoed user message is committed to the store. The moment that delta lands, all.length > 0, so the pending message is dropped — and the assistant's reply briefly renders as the first visible message with the user's prompt missing, until the echo catches up.

Reproduction

  1. Open an existing session that already has messages.
  2. Send a new message and watch the message list as the response starts streaming.
  3. Observed: the just-sent user message can flicker out and the streaming assistant reply appears first, until the server echo arrives.
  4. With this change: the pending user message stays put until its own echoed entry shows up.

Fix

Gate on the absence of an actual user message (all.some(m => m.type === 'user')) rather than an empty store, and append the pending message after existing content so it remains visible until its echo is committed.

npm run typecheck and eslint pass. Small change in useChatSessionState.ts.

Summary by CodeRabbit

  • Bug Fixes
    • Improved chat message display reliability by fixing a race condition where user messages could temporarily disappear when assistant responses arrived quickly. Messages now remain visible throughout the conversation.

The pending (optimistic) user message was only shown while the store was
empty (all.length === 0). On an existing session, the first realtime
event after sending can be a stream_delta (assistant content) that
arrives before the echoed user message is committed to the store. As
soon as that delta lands, all.length > 0, the pending message is
dropped, and the assistant response briefly renders as the first
visible message with the user's prompt missing.

Gate on the absence of an actual user message instead of an empty store,
and append the pending message after existing content so it stays put
until its own echo appears.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 569c6f3d-ede5-4347-aa6d-560f7fbbdc64

📥 Commits

Reviewing files that changed from the base of the PR and between 6a53c31 and 4af157a.

📒 Files selected for processing (1)
  • src/components/chat/hooks/useChatSessionState.ts

📝 Walkthrough

Walkthrough

The chatMessages derivation logic is updated to keep a pending user message visible until the store echoes it, preventing race conditions where streamed assistant content arrives before the user message.

Changes

Chat Pending Message Race Condition Fix

Layer / File(s) Summary
Pending message visibility on race conditions
src/components/chat/hooks/useChatSessionState.ts
Broadened the pending user message display condition from "no store messages exist" to "no user message exists in derived messages"; when that condition holds, the hook returns store-derived messages plus the pending user message to handle race conditions where streamed assistant content may arrive before the echoed user message.

Possibly related issues

  • siteboon/claudecodeui#794: Addresses the same optimistic-user-message reconciliation problem where pending user messages disappear when server-echoed messages are delayed relative to streamed assistant content.

Possibly related PRs

  • siteboon/claudecodeui#461: Both PRs modify useChatSessionState.ts message-sync derivation logic to prevent race/batching timing from causing pending or just-sent user messages to disappear before the store-echoed message is available.

Suggested reviewers

  • viper151
  • blackmammoth

Poem

🐰 A message that races ahead, now stays in view,
Till the server echoes back what the user knew,
No more vanishing acts mid-stream so fast,
Pending and patient—the cure will last! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: keeping a pending user message visible until its echoed version arrives from the server, which directly addresses the race condition bug in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@blackmammoth

Copy link
Copy Markdown
Member

Hey @bourgois, tried replicating this on main but couldn't get the issue described here. I used claude and codex for testing purposes. Can you check if the issue still exists?

@bourgois

Copy link
Copy Markdown
Contributor Author

Thanks for checking @blackmammoth — you're right, and the fault is in my repro.

I traced it: pendingUserMessage is only ever set for the first message of a brand-new session (addMessage only calls setPendingUserMessage when activeSessionId is null). For any existing session the message goes straight to the store, so the pendingUserMessage / all.length === 0 branch is never hit. My reproduction described an existing session, which can't exercise this path at all — so "couldn't reproduce" is correct.

The only real window is a sub-frame timing race in the new-session path (between the backend assigning a session id and the pending message being flushed to the store), which isn't reliably reproducible by hand and isn't worth your review time.

Closing this. Sorry for the noise. #854, #855, and #862 stand on their own.

@bourgois bourgois closed this Jun 10, 2026
@bourgois bourgois deleted the fix/pending-message-race branch June 10, 2026 16:28
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