{"record":{"id":"3eb3153793d764c1","repo":"gastownhall/beads","slug":"legacy-sqlite-source-changed-while-sealing","errorCode":null,"errorMessage":"legacy SQLite source changed while sealing","messagePattern":"legacy SQLite source changed while sealing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/migration/legacysqlite/reader.go","lineNumber":157,"sourceCode":"\t\tif err == nil {\n\t\t\terr = fmt.Errorf(\"sealed legacy SQLite database does not match source fingerprint\")\n\t\t}\n\t\treturn fail(err)\n\t}\n\tif before.wal.exists {\n\t\tif copied, err := fingerprintFile(filepath.Join(dir, \"legacy.db-wal\"), true); err != nil || copied.digest != before.wal.digest {\n\t\t\tif err == nil {\n\t\t\t\terr = fmt.Errorf(\"sealed legacy SQLite WAL does not match source fingerprint\")\n\t\t\t}\n\t\t\treturn fail(err)\n\t\t}\n\t}\n\tafter, err := fingerprintSource(resolved)\n\tif err != nil {\n\t\treturn fail(err)\n\t}\n\tif !sameSet(before, after) {\n\t\treturn fail(fmt.Errorf(\"legacy SQLite source changed while sealing\"))\n\t}\n\treturn sealedDB{dir: dir, db: filepath.Join(dir, \"legacy.db\"), source: resolved}, nil\n}\n\nfunc fingerprintSource(path string) (sourceSet, error) {\n\tdb, err := fingerprintFile(path, true)\n\tif err != nil {\n\t\treturn sourceSet{}, err\n\t}\n\twal, err := fingerprintFile(path+\"-wal\", false)\n\tif err != nil {\n\t\treturn sourceSet{}, err\n\t}\n\tif _, err := fingerprintFile(path+\"-shm\", false); err != nil {\n\t\treturn sourceSet{}, err\n\t}\n\tjournal, err := fingerprintFile(path+\"-journal\", false)\n\tif err != nil {","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/migration/legacysqlite/reader.go#L139-L175","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\n$ bd migrate --legacy ./live/beads.db ...   # while bd sync runs\n// error: legacy SQLite source changed while sealing\n// after\n$ bd sync stop && bd migrate --legacy ./live/beads.db ...","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// single safe retry: quiesce, then re-run Export\nif err := legacysqlite.Export(ctx, src, out, os.Stdout); err != nil {\n\tif strings.Contains(err.Error(), \"changed while sealing\") && !retried {\n\t\tstopWriters(src) // stop daemons/sync; then retry exactly once\n\t\tretried = true\n\t\treturn legacysqlite.Export(ctx, src, out, os.Stdout)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["sqlite","migration","race","consistency"],"backgroundTag":"source-changed-during-snapshot","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}