Skip to content

migration: V1 1.19.4 sessions not migrated to session_v2 #59517

Closed as duplicate

Description

@willn1337

Summary

Upgrading from OpenCode V1 1.19.4 to V2.1.4 silently left 15 sessions invisible. Their rows are still present in the legacy session, message, and part tables, but no corresponding rows were written to session_v2, so the session list, session picker, and session export cannot see them. Every affected session was created by V1 1.19.4; all 211 sessions from earlier V1 versions migrated correctly.

Environment

  • opencode version: 2.1.4 (upgraded from 1.19.4)
  • OS: Windows 11 Home, Microsoft Windows NT 10.0.26200.0
  • Terminal: TERM=xterm-256color (no TERM_PROGRAM / COLORTERM)
  • Shell: C:\Windows\system32\cmd.exe (invoked from PowerShell)
  • Install/channel: latest (npm global install, @opencode/cli 2.1.4)
  • Active plugins: none (only $schema in ~/.config/opencode/opencode.jsonc)

Reproduction

  1. Using OpenCode V1 1.19.4, create sessions in several different project directories.
  2. Upgrade to OpenCode 2.1.4 and start the app so the V1->V2 migration runs.
  3. Run opencode session list or open the session picker for an affected directory.
  4. The affected sessions are absent. opencode session export <session-id> returns Session not found: <id>.

Expected Behavior

All V1 sessions are migrated into session_v2 and visible in the session list, with their messages and tool output intact.

Actual Behavior

15 sessions remain only in the legacy tables and are unreachable through V2's read paths.

Read-only queries against opencode.db before recovery:

select count(*) from session where id not in (select id from session_v2);
-- 15

Correlation:

  • All 15 affected rows have session.version = '1.19.4'.
  • All 211 rows with earlier versions (1.15.x through 1.18.x) have matching session_v2 rows.
  • The affected sessions were spread across four different directories and five project_id values (including global), so directory/project does not appear to be the discriminator.
  • storage/migration contains 2; the migration appears to have completed without reporting an error.
  • Message and part data is fully intact. Example: one affected session had 192 message rows, 739 part rows, 214 tool calls, and 2 inline image attachments.

There is no warning or log entry indicating sessions were skipped.

Additional Context

Workaround used: build a projected transcript from the legacy session / message / part rows and re-import it through POST /api/experimental/session/import. Importing with the original info.id preserves the session ID, and parent sessions are imported before children. All 15 sessions were recovered this way and round-trip correctly through session export (verified message counts, tool counts, and attachment sizes).

One gotcha when replaying tool calls through import: the stored tool item's executed flag means "executed by the provider", not "has a result". Setting executed: true for locally executed tools makes the compiler place their results inside the assistant message, and the next prompt fails with OpenAI Chat assistant messages only support text, reasoning, and tool-call content for now before any request is sent. Normal V2 transcripts store executed: false for these items; with that value the restored sessions resume normally. Both errors are hard to diagnose because neither names the offending session or message.

Suggested fix: after the V1->V2 projection pass, compare session against session_v2 and migrate or report any remaining rows, rather than completing silently. A startup warning with the count and IDs would have made this immediately diagnosable.

Related minor observations:

  • session export cannot read legacy rows, so the affected sessions returned 404 before recovery.
  • session import is strict about required fields; a partial payload fails with an opaque Bad Request (the detailed InvalidRequestError message is only visible when calling the API directly).

Activity

  1. opencode-agent commented on Oct 27, 2026

    @opencode-agent
    Contributor

    Thanks for the very detailed report, and for writing up the import-based recovery, including the executed flag gotcha.

    I think this is a duplicate of #50130. The V1→V2 import runs only once. As soon as a V2 binary finishes it, the migration.v1-v2 key in kv is set to {"phase":"completed"} and the import never runs again. Any sessions a V1 client creates in the same opencode.db after that stay in the legacy session / message / part tables and never reach session_v2, so you get Session not found with no warning. The import code copies every legacy session row that exists when it runs, so sessions missing from session_v2 entirely most likely did not exist yet at that point. All 15 of yours are from the newest V1 version (1.19.4) and all older ones migrated, which fits that. #58840 had the same setup (V1 1.19.4, for example a V1 desktop app's bundled server, running alongside V2 2.1.4 on Windows).

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

    Your idea of checking session against session_v2 after the import and warning with the count and IDs fits #50130 and #54272 well.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different. For example, if you're sure no V2 binary (CLI, desktop or a bundled server) had started on that database before your last use of 1.19.4, please say so. If you can, also include the time_created / time_updated of the migration.v1-v2 row in kv and the time_created range of the 15 sessions.

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