{"record":{"id":"fe1931e4e72a6af0","repo":"can1357/oh-my-pi","slug":"backup-failed-integrity-check-backuppath","errorCode":null,"errorMessage":"Backup failed integrity check: ${backupPath}","messagePattern":"Backup failed integrity check: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/mnemopi/src/dr/recovery.ts","lineNumber":259,"sourceCode":"\tif (isSqliteFile(uncompressed)) {\n\t\twriteFileSync(tempPath, uncompressed, { flag: \"wx\" });\n\t\treturn;\n\t}\n\twriteGzippedSqlDump(uncompressed.toString(\"utf8\"), tempPath);\n}\n\nexport function restoreBackup(backupPath: string, dbPath?: string | null): RestoreResult {\n\tconst targetPath = dbPath ?? getDefaultPaths().dbPath;\n\tif (!existsSync(backupPath)) throw new FileNotFoundError(`Backup not found: ${backupPath}`);\n\n\tmkdirSync(dirname(targetPath), { recursive: true });\n\n\tconst uncompressed = gunzipSync(readFileSync(backupPath));\n\tconst tempPath = restoreTempPath(targetPath);\n\tlet replacedTarget = false;\n\ttry {\n\t\twriteRestoreCandidate(uncompressed, tempPath);\n\t\tif (!verifyIntegrity(tempPath)) throw new Error(`Backup failed integrity check: ${backupPath}`);\n\t\tsnapshotCurrentDatabase(targetPath);\n\t\trenameSync(tempPath, targetPath);\n\t\treplacedTarget = true;\n\t\tremoveSqliteSidecars(targetPath);\n\t\tconst integrity = verifyIntegrity(targetPath);\n\t\tif (!integrity) throw new Error(`Restored database failed integrity check: ${backupPath}`);\n\t\treturn {\n\t\t\trestored: true,\n\t\t\tbackup_used: backupPath,\n\t\t\tdatabase_path: targetPath,\n\t\t\tintegrity_check: integrity,\n\t\t};\n\t} catch (error) {\n\t\ttry {\n\t\t\trmSync(tempPath, { force: true });\n\t\t} catch {\n\t\t\t// Preserve the restore failure.\n\t\t}","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/mnemopi/src/dr/recovery.ts#L241-L277","documentation":"restoreBackup() verifies every candidate database with an SQLite integrity check before replacing the live database. If the gunzipped backup file at tempPath fails verifyIntegrity, it throws this error naming the original backup path. The live target database is untouched in this case because the snapshot/rename happens after verification.","triggerScenarios":"Calling restoreBackup(backupPath, targetPath) where the decompressed backup is corrupt, truncated, or not a valid SQLite database file (verifyIntegrity returns false).","commonSituations":"Backups produced by a crashed backup job, gzip files corrupted by partial writes or disk-full, copying .db files while SQLite had active WAL sidecars, restoring across incompatible SQLite versions, or manual edits to a backup file.","solutions":["Verify the backup file independently: run `sqlite3 <gunzipped-file> 'PRAGMA integrity_check;'` to see the specific corruption.","Regenerate a fresh backup with backupDatabase() and retry the restore.","Check disk space and the gzip file (`gzip -t`) for truncation/corruption.","Restore an older backup file from the same directory that passes integrity checks."],"exampleFix":"// before\nconst result = restoreBackup('/backups/mnemopi_backup_latest.db.gz', dbPath);\n// after\nimport { verifyIntegrity } from './recovery';\nconst uncompressed = gunzipSync(readFileSync('/backups/mnemopi_backup_latest.db.gz'));\nwriteFileSync('/tmp/candidate.db', uncompressed);\nif (!verifyIntegrity('/tmp/candidate.db')) {\n  console.error('Backup corrupt, picking older backup');\n}\nconst result = restoreBackup('/backups/mnemopi_backup_latest.db.gz', dbPath);","handlingStrategy":"validation","validationCode":"import { existsSync, readFileSync } from 'node:fs';\nimport { gunzipSync } from 'node:zlib';\nfunction backupLooksValid(path) {\n  if (!existsSync(path)) return false;\n  try {\n    const header = gunzipSync(readFileSync(path)).subarray(0, 16);\n    return header.toString('utf8').startsWith('SQLite format 3');\n  } catch { return false; }\n}\nif (!backupLooksValid(backupPath)) throw new Error(`Backup unreadable: ${backupPath}`);\nconst result = restoreBackup(backupPath, targetPath);","typeGuard":"function isNonEmptyString(v) { return typeof v === 'string' && v.length > 0; }\nfunction canRestore(backupPath) {\n  return isNonEmptyString(backupPath) && backupPath.endsWith('.db.gz');\n}","tryCatchPattern":"try {\n  const result = restoreBackup(backupPath, targetPath);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Backup failed integrity check')) {\n    // pick next-oldest backup; live DB untouched\n  } else throw err;\n}","preventionTips":["Run PRAGMA integrity_check on backups right after creating them","Use SQLite's backup API / VACUUM INTO instead of copying live db files","Keep at least two generations of backups and test-restores regularly","gzip -t every archive after writing to catch truncation"],"tags":["sqlite","integrity-check","backup-restore","data-corruption"],"backgroundTag":"database-integrity-check-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}