gastownhall/beads · error
legacy SQLite source changed while sealing
Error message
legacy SQLite source changed while sealing
What it means
After copying the db and WAL into the sealed temp directory, seal re-fingerprints the entire source set (db, wal, journal) and compares it with the pre-copy fingerprints, including size, mtime, digest, and inode identity (os.SameFile). This error means the source changed — in existence, content, or identity — between the before and after fingerprints, so the sealed snapshot may mix old and new states. Export aborts to guarantee exports are point-in-time consistent.
Solutions
- Quiesce all writers to the legacy database and retry the export
- Run the export once, not concurrently with other bd commands against the same repo
- Exclude the database path from sync/backup/antivirus scanning during export
- Export from a frozen copy: cp beads.db beads.db-wal (while stopped) to a local dir and export that
Example fix
// before $ bd migrate --legacy ./live/beads.db ... # while bd sync runs // error: legacy SQLite source changed while sealing // after $ bd sync stop && bd migrate --legacy ./live/beads.db ...
Defensive patterns
Strategy: retry
Try / catch
// single safe retry: quiesce, then re-run Export
if err := legacysqlite.Export(ctx, src, out, os.Stdout); err != nil {
if strings.Contains(err.Error(), "changed while sealing") && !retried {
stopWriters(src) // stop daemons/sync; then retry exactly once
retried = true
return legacysqlite.Export(ctx, src, out, os.Stdout)
}
return err
} Prevention
- Quiesce all writers before export; treat this error as a signal of concurrent modification, not corruption
- Exclude the database path from sync clients, backups, and antivirus during export
- Don't run exports concurrently with other bd commands on the same repo
- Export from a frozen copy for unattended pipelines
When it happens
Trigger: Export -> seal: sameSet(before, after) is false because fingerprintSource(resolved) after the copy disagrees with the before snapshot on db, -wal, or -journal (any of: missing, size, mtime, digest, or same inode).
Common situations: A writer commits a transaction during the export; WAL auto-checkpoint rewrites the WAL; a sync client replaces the file (new inode); backup/AV tools touch mtime; a journal file appears mid-run.
Related errors
- blocking dependency -> conflicts with parent-child hierarchy
- comment created_at is zero for issue
- dependency created_at is zero for
- dependency created_by is empty for
- dependency -> crosses ephemeral storage
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3eb3153793d764c1.
Report an issue: GitHub.
Appendix: source
Thrown at internal/migration/legacysqlite/reader.go:157
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{}, err
}
wal, err := fingerprintFile(path+"-wal", false)
if err != nil {
return sourceSet{}, err
}
if _, err := fingerprintFile(path+"-shm", false); err != nil {
return sourceSet{}, err
}
journal, err := fingerprintFile(path+"-journal", false)
if err != nil {View on GitHub (pinned to 71377f2769)