You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TUI renders a user message twice when a prompt reuses an existing messageID #50945
Sending a prompt with the messageID of an existing message rewrites that message with a new time.created, but keeps the same id. The TUI sync store keys messages by time.created + id (packages/tui/src/context/sync.tsx, messageKey). When the second message.updated arrives, the lookup misses, so the TUI inserts a second row for the same id. Parts are keyed by message id, so both rows render the same content.
Storage is correct: the session has one message with one part. Reloading the session shows it once.
With opencode attach $B -s $SID open, "hello" appears in two user rows. The event stream shows two message.updated events for $MID with different time.created values.
Suggested fix
Key TUI messages by id alone. Ascending message ids already sort by creation time, so time.created in the key only breaks the lookup when a message is rewritten. Alternatively, keep the original time.created when a prompt reuses an existing messageID.
Confirmed on dev: packages/tui/src/context/sync.tsx binary-searches the per-session message list by messageKey = time.created + id. When a prompt reuses an existing messageID, the second message.updated has a new time.created, the lookup misses, and the message gets spliced in again as a second row.
Plan: in the message.updated handler, look up an existing entry by id first. If one is found, replace it and re-sort it into its creation-time position, so the ordering from #40994 stays intact. The creation-time binary search stays for genuinely new messages. The regression test goes in test/cli/cmd/tui/sync-live-hydration.test.tsx: emit message.updated twice for the same id with different time.created and assert that the session holds exactly one entry with the latest info.
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.
Activity
myagizmaktav commentedon Sep 24, 2026
I'd like to take this one.
Confirmed on
dev:packages/tui/src/context/sync.tsxbinary-searches the per-session message list bymessageKey = time.created + id. When a prompt reuses an existingmessageID, the secondmessage.updatedhas a newtime.created, the lookup misses, and the message gets spliced in again as a second row.Plan: in the
message.updatedhandler, look up an existing entry byidfirst. If one is found, replace it and re-sort it into its creation-time position, so the ordering from #40994 stays intact. The creation-time binary search stays for genuinely new messages. The regression test goes intest/cli/cmd/tui/sync-live-hydration.test.tsx: emitmessage.updatedtwice for the same id with differenttime.createdand assert that the session holds exactly one entry with the latest info.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊