Skip to content

fix(core): fail fast when DB schema is ahead of current runtime - #49225

Open
MdTanwer wants to merge 1 commit into
anomalyco:devfrom
MdTanwer:schema-version-guard
Open

MdTanwer wants to merge 1 commit into
anomalyco:devfrom
MdTanwer:schema-version-guard

Conversation

@MdTanwer

@MdTanwer MdTanwer commented Sep 15, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #49177
Closes #38471
Closes #35403

Type of change

  • Bug fix

What does this PR do?

Desktop, CLI (opencode-ai via npm), and plugins share a single SQLite file (opencode.db). Any runtime that opens the file first can advance the schema via migrations. When an older binary opens the same file afterward, it used to silently succeed at startup and then crash mid-prompt with an opaque SQL error — SQL logic error, no such column: replacement_seq, etc. — depending on which direction the version skew went.

The fix is a single guard block added in applyOnly() inside packages/core/src/database/migration.ts, placed after the legacy journal seeding and before the apply loop. It computes:

unknownAhead = (IDs in DB migration table) − (IDs known to this binary)

If non-empty, the DB was migrated by a newer runtime and this binary can't safely use it. We call Effect.die immediately with a clear message that names the unknown migrations and suggests two remedies: upgrade the binary, or isolate with OPENCODE_DB.

This is consistent with how the rest of apply() handles invalid DB states — two other Effect.die calls already exist for different invariants.

All three related issues share the same root cause: no check for DB-ahead-of-binary. #49177 was CLI-ahead-of-Desktop, #38471 was Desktop-ahead-of-CLI (specifically the 20260622142730_simplify_session_context_epoch migration that dropped replacement_seq/revision/agent), and #35403 was CLI-ahead-of-plugin.

How did you verify your code works?

  • bun typecheck in packages/core → exit 0
  • Full monorepo typecheck via pre-push hook → 30 packages, all passing
  • Manually traced the code path: Database.nodelayerFromPathlayerDatabaseMigration.applyapplyOnly → guard fires before any SQL query runs

Screenshots / recordings

No UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

When Desktop, CLI, or a plugin migrate the shared opencode.db forward,
an older binary that opens the same file will now die at startup with a
clear error instead of silently proceeding and crashing inside a SQL
query handler.

The guard computes the set difference (completed IDs in DB) - (known IDs
in binary). A non-empty result means the DB was advanced by a newer
runtime; Effect.die surfaces the unknown migration IDs and suggests two
remedies: upgrade or isolate via OPENCODE_DB.

Closes anomalyco#49177
Closes anomalyco#38471
Closes anomalyco#35403
@kvnloo

kvnloo commented Sep 15, 2026

Copy link
Copy Markdown

Why this matters

Shared opencode.db across Desktop/CLI/plugins: a newer runtime migrates first, then an older binary opens “OK” and crashes mid-prompt with opaque SQL (#49177 #38471 #35403).

Evidence

Tip 6e9e0ab (base dev):

  • packages/core/src/database/migration.ts applyOnly() — after journal seed, before apply loop
  • unknownAhead = completed − knownIdsEffect.die listing unknown migrations + upgrade / OPENCODE_DB remedies
  • +14 lines, one file; same die-on-invariant style as existing apply() guards
  • CI: standards / compliance / duplicates success

Confirms scope — fail fast on DB-ahead-of-binary. Happy to help land as-is.

Ask (design, light) — prospective

  1. Is multi-runtime SQLite skew a class every entrypoint must detect, or only migration apply?
  2. For never-again: die on unknown migration IDs (this PR), or an explicit schema generation / min-runtime stamp at open?

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

Labels

None yet

Projects

None yet

2 participants