anomalyco / anomalyco/opencode
server: V1 sessions created after migration.v1-v2 completes are never imported
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
OpenCode 2 performs a one-time V1 to V2 session import whose state lives in kv['migration.v1-v2']. Once that key is {"phase":"completed"}, any V1 session that appears in the same database afterwards is never imported: it stays in the legacy session / message / part tables, is absent from session_v2, GET /api/session/{id} returns 404, and desktop tabs that reference it show "the session this tab points to no longer exists on this server".
I hit this because I kept using a V1 client (1.18.31) alongside the V2 desktop app. The migration completed on 2026-09-17 (first V2 startup, 2.0.3); 11 sessions created with V1 after that were invisible in V2. The same first run also skipped 29 V1 sessions that already existed in the database. The data was never lost, but nothing surfaced the problem.
Plugins
oh-my-openagent@latest
OpenCode version
2.0.10 (desktop bundled CLI; migration.v1-v2 state was created by 2.0.3, 2.0.5 and 2.0.8 were also installed in between)
Steps to reproduce
- Create sessions with a V1 client (1.18.x) in
~/.local/share/opencode/opencode.db. - Let a V2 build finish a startup so
kv['migration.v1-v2']becomes{"phase":"completed"}(here: 2.0.3, even though 29 V1 sessions were already present). - Create new sessions with the V1 client against the same database.
- Open the V2 desktop app: the new sessions are missing.
opencode api get /api/session/<id>returns 404,opencode api get /api/experimental/migration/v1keeps returning{"status":"completed"}.
Expected behavior
The one-time import should only be marked completed after it has actually drained existing V1 history, or it should notice new V1 rows and run/resume instead of reporting completed forever. At minimum, unimported V1 sessions should be surfaced instead of making session tabs 404.
Actual behavior
kvrowmigration.v1-v2 = {"phase":"completed"}, created 2026-09-17 10:19:31.- 40 V1 sessions existed in
session(versions 1.18.31 x11, 1.4.7 x20, 1.2.24 x9), 0 of them were copied tosession_v2. - All 19901 rows in the
eventtable still carry the defaultcreated = 0, suggesting thephase === undefinedbranch (clear events) did not do what it looks like it should on this V1 database. - Log: the client kept polling
GET /api/session/ses_...and got 404 while the row existed insession.
Additional context
Recovery/workaround that brought everything back without data loss:
- Back up
opencode.db. UPDATE kv SET value='{"phase":"sessions"}' WHERE key='migration.v1-v2';- Boot any V2 server (e.g.
opencode api --standalone get /api/experimental/migration/v1) so the importer resumes. - It copied all 40 sessions and converted 2211 V1 messages into
session_messagecorrectly, then wrote{"phase":"completed"}again.
So the import/conversion logic itself works; the completion/"already completed" gate is the problem. In the shipped binary the state is read with an early if (state.phase === "completed") return, and loose_psylocke already knows how to detect V1-only history (Previous V2 database contains V1-only session history), but there is no equivalent check or re-arm for V1 sessions newer than the last completed import.
OS
Arch Linux, kernel 7.2.6-arch2-1 (x86_64)
Terminal
OpenCode desktop app 2.0.10 (Electron); same behavior visible from the CLI (opencode api).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the migration.v1-v2 state handling, especially the completed-state gate, and trace the /api/experimental/migration/v1 entry point against the session and session_v2 tables. Reproduce with V1 sessions created after completion, then verify that unimported sessions are imported or surfaced and that completion is not reported while they remain; rerun the documented recovery command to confirm conversion still works.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100