Skip to content

Session history replay truncates after edit-and-resend of a message on legacy-schema long sessions (data intact, UI stops rendering earlier checkpoint) #61189

Open

Description

@LDecem

Description


Title: Session history replay truncates after edit-and-resend of a message on legacy-schema long sessions (data intact, UI stops rendering earlier checkpoint)

Labels (suggestion): bug, desktop, maybe triage


Bug description

In a long-running session (2 days old, legacy storage structure predating the part migration), using the retract / edit-and-resend action on an earlier user message caused the session history replay (UI) to stop rendering at a much earlier checkpoint — every agent message after that old checkpoint is invisible in the UI, including the re-sent message itself.

Important: this is display-only. The agent's internal context kept working correctly, all subsequent tool calls and file changes happened, and everything is present in local storage (opencode.db). Nothing is lost — only the history replay in the UI no longer shows the tail of the conversation.

Reproduction in a fresh short session failed (session stored in the new part schema), which suggests the trigger is the combination of (a) legacy session_message-stream sessions and (b) the revert flow created by edit-and-resend.

Environment

  • OpenCode: 2.2.5 (desktop client, latest channel)
  • OS: Windows 11 x64
  • Client: desktop app; HTML preview opened via Review pane
  • Affected session profile: 2 days of activity, ~30 user/agent exchanges, 22+ background subagent dispatches (incl. 1 task_cancel, 1 task_revive), local sqlite opencode.db grew to ~11.8 GB

Steps to reproduce

  1. Continue a long-running session created before the session_part/part storage migration (its messages live in the legacy session_message stream table), with heavy tool/subagent activity.
  2. Use retract / edit-and-resend on an earlier user message (a session.revert.stage → session.revert.commit flow; the re-sent message gets a new message id).
  3. Continue normally for 10+ exchanges (agent replies, tools run).
  4. Reopen the session view / scroll back through history.
  5. History replay stops at an agent message from before the revert; all later messages (including the re-sent one and subsequent agent replies) are missing from the UI.

Expected vs Actual

  • Expected: replay shows the resent message and everything after it.
  • Actual: replay stops at a stale checkpoint; tail of the conversation is invisible in UI, while the agent continues working and storage records everything.

Evidence from local storage (SQLite opencode.db, read-only queries)

For the affected session:

-- message stream intact and contiguous:
SELECT COUNT(*) FROM session_message WHERE session_id = '<affected-session-id>';  -- 503 rows, seq spans normally
-- legacy schema: no rows in the migrated structures for this session:
SELECT COUNT(*) FROM message      WHERE session_id = '<affected-session-id>';  -- 0
SELECT COUNT(*) FROM session_part WHERE session_id = '<affected-session-id>';  -- 0

Server log confirms the flow on the affected session:

session.revert.stage   messageID=msg_<retracted-message-id>   (retract action)
session.revert.commit                                          (few minutes later)
-- subsequent exchanges got new message ids and appended to the stream normally

So the launcher-side data is complete; the replay UI does not render it.

Hypothesis (unverified)

The history replay renderer reads the migrated part-based chain (or an inconsistent subset) instead of the legacy session_message stream for sessions that still live in the old schema — reverting/branching on such a session moves the expected chain beyond what the renderer walks, so it renders up to a stale checkpoint.

Impact & workaround

  • Impact: audit/history view only (display misled the user into thinking the conversation was lost). Agent execution, storage, and disk artifacts are unaffected; the current-position view (newest messages) still works while streaming.
  • Suggested workaround: avoid edit-and-resend on legacy sessions; start a fresh message instead.

Activity

  1. opencodejr commented on Nov 2, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report. I couldn't reproduce this here: it happens in the Windows desktop app, which our automated sandbox can't run, so it needs a maintainer with the desktop app.

    A possible cause: after a revert is committed, the desktop client decides where to cut the loaded history by comparing message IDs as text, not by their real order. That cuts in the wrong place in some sessions and can hide newer messages, which would match what you saw. #57581 (an open PR, not merged yet) changes this to use the real message order. One correction: session_message is the current table, not a legacy one, so the old-schema explanation in the report probably isn't the cause.

    Two things would help:

    • Was this session forked from another session, or were any messages sent from the iOS app?
    • After fully restarting the desktop app and reopening the session, are the missing messages still hidden?

    Related:

  2. LDecem commented on Nov 2, 2026

    @LDecem
    Author

    Two things would help:

    * Was this session forked from another session, or were any messages sent from the iOS app?
    
    * After fully restarting the desktop app and reopening the session, are the missing messages still hidden?
    

    It happened in a master session, not a forked one. Not-relevant to any iOS app nor Android app.
    Restarting or re-opening not done yet - tasks still running. Maybe later.

  3. sayan1999 commented on Nov 2, 2026

    @sayan1999

    I have same behaviour in my Mac Os Opencode TUI

    In my case observation:
    Apparently the internal context stays intact and a restrat of the opencode session revives the right history on the UI as it should be at.

  4. dhaern commented on Nov 9, 2026

    @dhaern

    Same here on the TUI with OpenCode 2.2.6 on Linux, in long sessions that were never forked and have no iOS messages. In my case plugins are the trigger: oh-my-opencode-slim posts synthetic messages with IDs like msg_omos_<uuid>, and magic-context posts reminders with msg_<24 random hex>. Compared as strings, both sort after every native msg_<time> ID. If one of them sits anywhere before the user message you revert, the TUI hides it while the revert is staged, and on commit the session.revert.committed handler cuts the loaded transcript from that synthetic message onward. The server deletes by seq, so the database and the model's context stay correct, and as reported above, reopening the session restores the view. In my local database 3,061 of 14,281 user messages have an earlier message whose ID sorts after theirs, so any long session with these plugins can hit this. #57581 orders by position and covers both the TUI filter and the committed cut.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions