Skip to content

migration: V1 to V2 session migration drops assistant messages (reason=invalid-message) #55991

Closed as duplicate

Description

@skquievreux

Description

The V1 → V2 session migration projected only user messages for older sessions into the new session_message table. All assistant messages were skipped with reason=invalid-message and stayed in the legacy message/part tables, where the V2 client, API and session export no longer see them.

Result after migrating a large V1 install: of 336 sessions, 127 have fewer messages in V2 than in V1 — 9,353 messages missing from the V2 view. In 126 of those sessions there are zero assistant messages, so an old session renders as a bare list of user prompts.

The pattern is the V1 message shape, not the session: rows that already used the newer V1 shape (agent, model, summary) migrate fine; rows in the classic V1 assistant shape (parentID, providerID, modelID, mode, path, cost, tokens, finish) are rejected.

Concrete example (ses_29790b6c9ffeu67nLM0EU5zTdT):

legacy message V2 session_message
assistant 752 0
user 60 50
synthetic – 5

opencode session export ses_29790b6c9ffeu67nLM0EU5zTdT returns exactly those 55 records (50 × user, 5 × synthetic) — no assistant content at all.

The data itself is not lost, only unprojected: the assistant text is still complete in the legacy part rows (21,335 × text, 16,425 × reasoning, 31,367 × tool parts) and in ~/.local/share/opencode/storage/message/*.json.

Log evidence (one migration run, 2026-10-14T11:27, run=80841d95):

level=WARN message="Skipped V1 migration row" reason=invalid-message sessionID=ses_4fff14cd0ffeFqBQMcSf47Skxx messageID=msg_b000eb371001hUYBzY2ry54wiy role=server

9,333 such lines across 128 session IDs; the migration still reports itself as complete (entry in the migration table), so it never retries. Still reproducible on 2.0.30 — the upgrade from 2.0.29 changed nothing, because the migration is one-shot.

Steps to reproduce

  1. Start from a V1 install whose sessions contain assistant messages in the older V1 shape (pre-agent/model, i.e. with parentID/providerID/modelID/finish).
  2. Update to V2 and start it, letting the V1 → V2 migration run.
  3. Open an old session in the TUI, or run opencode session export <session-id>.
  4. Check ~/.local/share/opencode/log/opencode.log for Skipped V1 migration row and compare row counts:
    SELECT count(*) FROM message WHERE session_id=? vs. SELECT count(*) FROM session_message WHERE session_id=?.
  5. Observe: only user prompts are visible, assistant replies are missing.

Expected behavior

The V1 → V2 session migration projects the full history of existing sessions, so old sessions keep their assistant replies in V2 — the same way it already handles messages in the newer V1 shape. If some rows genuinely cannot be converted, they should not be silently dropped from the V2 view without a recoverable path.

Actual behavior

For 127 older sessions, all assistant messages are absent from session_message and therefore from the TUI/API/export. They are only reported as WARN "Skipped V1 migration row" reason=invalid-message and remain readable solely through the legacy tables.

Additional context

  • Version: 2.0.30 (first observed on 2.0.29)
  • Database: ~/.local/share/opencode/opencode.db — session_message 26,601 rows, legacy message 32,573 rows, part 130,767 rows
  • Not an orphan/corruption problem: 0 sessions without messages, 0 orphaned messages, 0 broken parent_id references. Session persistence, session list, session export, child sessions and restarts all behave correctly — only the historical assistant content is missing from V2.
  • The DB file also carries ~604 MB of free pages after the migration (freelist_count), which may be related if rows are rewritten during the same run.

Activity

  1. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report and the row counts. I think this is a duplicate of #53971. There, the V1 → V2 migration checks each legacy message against the current V1 schema, which requires agent (plus parentID on assistant messages and model on user messages). Messages written in the older V1 shape (mode/modelID/providerID/path/cost/tokens, no agent) fail that check. They are skipped with reason=invalid-message, and the migration is still marked as complete. That matches what you're seeing. PR #53996 is open to fill in those fields during migration instead of dropping the rows.

    Possible duplicates and related issues, ordered from most to least likely:

    If you try one of the manual workarounds, back up opencode.db first. As noted in #53971 and #52226, re-running the migration can overwrite V2 history that was added to migrated sessions after the first run.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

  2. skquievreux commented on Oct 16, 2026

    @skquievreux
    Author

    Thanks — agreed, this is the same root cause, and closing it as a duplicate of #53971 is fine. Two data points from a second, independent database (Windows, migrated 2026-10-14 on 2.0.29, unchanged on 2.0.30) in case they help #53996:

    One gap that #53996 does not close: it changes the migrator, but the migration is gated by kv['migration.v1-v2'], which reads {"phase":"completed"} on every install that has already migrated. Updating 2.0.29 → 2.0.30 re-projected nothing here, so a fixed migrator will not repair an existing database on its own — those users still have to delete that marker to force another run.

    If that manual reset is the intended remedy, could it be stated in the release notes or migration docs, next to the existing warning that a re-run wipes V2-appended history (#52226)? A one-line note would save every affected user from repeating this same research.

  3. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for confirming, and for checking against the database rather than the log. Knowing that every affected row on your install has mode and only agent is missing helps with #53996.

    You're right about the gate. On current dev, packages/core/src/database/v1-migration.bun.ts returns early whenever kv['migration.v1-v2'] has phase: "completed", and #53996 only changes how rows are converted. It doesn't re-check or re-run installs that have already migrated. As it stands, the fix alone wouldn't repair existing databases, and the only way to get those rows back is still a manual reset with the warning about V2 history added after the first run (#52226).

    This issue is going to close as a duplicate. Please post that point, and the request for a release-note or migration-docs entry, on #53971 or PR #53996, where maintainers and the PR author are working on the fix. I can't say whether they'll handle it with a one-time re-run, a migration that repairs only the affected sessions, or documentation.

    The bot will still close this issue as a duplicate of #53971 in 1 day.

  4. opencode-agent commented on Oct 17, 2026

    @opencode-agent
    Contributor

    Closing this as a duplicate of #53971, as agreed above. Please follow #53971 and PR #53996 for the migration fix.

    If you haven't yet, please post your point there about installs that are already marked migration.v1-v2: completed. Those won't be re-projected by a fixed migrator on their own, so it may need a one-time re-run or a note in the docs. The people working on the fix will see it there. Thanks again for the detailed counts from your database.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions