Repository navigation
Description
Summary
The V1→V2 data migration permanently stalls when the legacy session table contains a row with directory = '.' (relative). The migration copies the row into session_v2 verbatim, then the re-read through the strict directoryColumn schema throws Path is not absolute: ., the whole transaction (including the cursor update) rolls back, and every subsequent startup retries the same row and fails identically. The migration can never complete on its own.
Environment
- opencode version: 2.1.1
- OS: Windows 10.0.26300 (win32 x64)
- Terminal: VS Code integrated terminal (TERM_PROGRAM=vscode, TERM=xterm-256color)
- Shell: cmd.exe
- Install/channel: latest
- Active plugins: superpowers (git+https://github.com/obra/superpowers.git)
Reproduction
- Have a database whose legacy
sessiontable contains at least one row with a non-absolutedirectory. In my case: 13 rows withdirectory = '.'— "ACP Session <uuid>" sessions created in Feb 2026 on v1.1.53 via the ACP integration (Zed), which recordedcwdas.. Verify with:SELECT id, directory FROM session WHERE directory = '.';
- Run any 2.x server so the V1→V2 data migration starts (
kvkeymigration.v1-v2, phasesessions). - The migration walks the legacy table
ORDER BY id DESCwith a cursor, hits the bad row, and dies. It aborts at every startup from then on.
Expected Behavior
- A single legacy row with an invalid/relative
directoryshould not be able to wedge the migration permanently. The migration should either normalize the value (e.g. resolve.against a sensible base) or skip-and-log the row, liketransformSessionalready does for off-format message/part rows. directoryColumnalready deliberately tolerates the empty string ("Legacy sessions may persist an empty directory"), but.hits theabsolute()throw inpackages/core/src/database/path.ts.
Actual Behavior
Every startup logs:
level=ERROR message="V1 migration failed" cause="Cause([Die(EffectDrizzleQueryError: Failed query: select \"id\", \"project_id\", ... from \"session_v2\" where \"session_v2\".\"id\" = ?\nparams: [redacted] (cause: Cause([Fail(Error: Path is not absolute: .)])))])" role=server
The TUI shows a "Data migration failed" toast on every launch. The kv state stays at {"phase":"sessions","cursor":"..."} pointing at the last successfully processed row, so the same bad row is retried (and fails) on every boot.
Root Cause (from source, v2.1.1 / current main)
In packages/core/src/database/v1-migration.bun.ts, the sessions phase loop per row:
- Updates the
kvcursor tonextID.id(inside the transaction). INSERT OR IGNORE INTO session_v2 (...) SELECT id, ..., directory, path, ... FROM session WHERE id = ${nextID.id}— copiesdirectoryverbatim, with no normalization.- Re-reads the row through Drizzle:
tx.select().from(SessionTable).where(eq(SessionTable.id, ...))— this is the failing query. directoryColumn.fromDrivercallsabsolute()inpackages/core/src/database/path.ts, which throwsPath is not absolute: ${input}for'.'(posix.isAbsolute('.') === falseandisWindowsStoragePath('.') === false).- The failure dies the transaction, so the cursor update from step 1 rolls back too — the migration is stuck on the same row forever.
Workaround (verified)
Back up the database, repair the legacy rows, restart the service:
UPDATE session SET directory = 'C:/Users/User' WHERE directory = '.';After restart the migration resumed from its cursor, processed the remaining 283 sessions, and completed: migration.v1-v2 → {"phase":"completed"}, all 383 legacy sessions present in session_v2, no further startup errors.
Additional Context
- Database: 751 MB, 383 legacy V1 sessions, 6,566 messages / 23,614 parts (all IDs well-formed, so nothing else in the pipeline was implicated).
- This is the same "one bad row aborts the whole migration" class as V1→V2 migration: one off-format message ID aborts it; migrated sessions stay invisible (stale project_id + path) #55663 and migration: V1 migration aborts on legacy message ID failing Session.Message.ID schema #54736, but triggered by an invalid
directoryinstead of a message ID. - The bug is still present on current
main— the migration copiesdirectoryverbatim and has no skip/normalize path for it. - Frequency: consistently, on every startup, until the legacy rows are repaired manually.
Activity
opencode-agent commentedon Oct 21, 2026
Thanks for the detailed report. I was able to reproduce this on 2.1.1, and it still happens on the latest
dev(aadd4d8).I tested it with
opencode servein a clean data directory on Linux, so this isn't specific to Windows:session/message/parttables with two sessions: a newer one with an absolute directory and an older one withdirectory = '.'.Both restarts logged
V1 migration failed ... Path is not absolute: .. Themigration.v1-v2cursor stayed on the last good session, and the.session never reachedsession_v2, so the migration can't get past that row. The script below reproduces it.Related issues:
Path is not absolute: .error from legacy ACP rows, but in the v1 session list (closed).Until this is fixed, your workaround (back up the database, then set those rows'
directoryto an absolute path) is the way to get the migration to finish.Reproduction script (repro.sh)
Run it:
bash repro.sh