gastownhall/beads · error
sealed legacy SQLite WAL does not match source fingerprint
Error message
sealed legacy SQLite WAL does not match source fingerprint
What it means
Identical integrity check to the db-fingerprint error but applied to the copied WAL sidecar (legacy.db-wal). After copying the source WAL into the sealed directory, its SHA-256 must match the WAL fingerprint taken before the copy; otherwise the sealed snapshot would not represent a consistent point-in-time state of the source. Export fails and cleans up the temp directory.
Source
Thrown at internal/migration/legacysqlite/reader.go:147
from, to string
present bool
}{{resolved, filepath.Join(dir, "legacy.db"), true}, {resolved + "-wal", filepath.Join(dir, "legacy.db-wal"), before.wal.exists}} {
if pair.present {
if err := copyFile(pair.from, pair.to); err != nil {
return fail(err)
}
}
}
if copied, err := fingerprintFile(filepath.Join(dir, "legacy.db"), true); err != nil || copied.digest != before.db.digest {
if err == nil {
err = fmt.Errorf("sealed legacy SQLite database does not match source fingerprint")
}
return fail(err)
}
if before.wal.exists {
if copied, err := fingerprintFile(filepath.Join(dir, "legacy.db-wal"), true); err != nil || copied.digest != before.wal.digest {
if err == nil {
err = fmt.Errorf("sealed legacy SQLite WAL does not match source fingerprint")
}
return fail(err)
}
}
after, err := fingerprintSource(resolved)
if err != nil {
return fail(err)
}
if !sameSet(before, after) {
return fail(fmt.Errorf("legacy SQLite source changed while sealing"))
}
return sealedDB{dir: dir, db: filepath.Join(dir, "legacy.db"), source: resolved}, nil
}
func fingerprintSource(path string) (sourceSet, error) {
db, err := fingerprintFile(path, true)
if err != nil {
return sourceSet{}, errView on GitHub (pinned to 71377f2769)
Solutions
- Ensure no process is using the legacy database (close bd sessions, stop daemons) before exporting
- If safe, checkpoint and remove the WAL first: run sqlite3 beads.db 'PRAGMA wal_checkpoint(TRUNCATE);' so no -wal exists during sealing
- Export from an offline snapshot copy of the database instead of the live file
- Serialize the export against any scheduled jobs that write to the database
Example fix
// before (live WAL-mode DB) $ bd migrate --legacy ./beads.db --output ./issues.jsonl // error: sealed legacy SQLite WAL does not match source fingerprint // after (checkpoint WAL while quiesced) $ sqlite3 beads.db 'PRAGMA wal_checkpoint(TRUNCATE);' $ bd migrate --legacy ./beads.db --output ./issues.jsonl
Defensive patterns
Strategy: validation
Validate before calling
// checkpoint the WAL while quiesced so no -wal exists during export
func checkpointWAL(dbPath string) error {
db, err := sql.Open("sqlite3", dbPath); if err != nil { return err }
defer db.Close()
_, err = db.Exec("PRAGMA wal_checkpoint(TRUNCATE)")
return err
} Try / catch
if err := legacysqlite.Export(ctx, src, out, os.Stdout); err != nil {
if strings.Contains(err.Error(), "WAL does not match source fingerprint") {
_ = checkpointWAL(src) // while no writers are active
// retry export once
}
return err
} Prevention
- Run PRAGMA wal_checkpoint(TRUNCATE) on a quiesced database so the -wal file is empty/absent during export
- Ensure no other process holds the database open in WAL mode during export
- Copy db+wal together offline and export from the copy
- Avoid export while auto-checkpointing writers are active
When it happens
Trigger: Export -> seal when before.wal.exists: the copied dir/legacy.db-wal digest differs from the pre-copy fingerprint. Happens when the source database is in WAL mode and a writer checkpoints/rewrites the -wal file during the copy window.
Common situations: A live bd process or another SQLite connection is writing to the WAL-mode legacy database while export runs; SQLite auto-checkpoint truncates/rewrites the WAL mid-copy; scheduled sync tools touching the sidecar.
Related errors
- sealed legacy SQLite database does not match source fingerpr
- legacy SQLite source changed while sealing
- legacy SQLite source %q must not be a symlink
- legacy SQLite source %q must be a regular file
- --output must not alias legacy SQLite source or sidecar
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2682be39740309c1.
Report an issue: GitHub.