can1357/oh-my-pi · error · FileNotFoundError

Backup not found: ${backupPath}

Error message

Backup not found: ${backupPath}

What it means

restoreBackup checks that the given backup file exists before decompressing and replacing the live database, throwing FileNotFoundError when backupPath doesn't exist. This prevents silently restoring from a missing file and destroying the current db with nothing.

Source

Thrown at packages/mnemopi/src/dr/recovery.ts:250

	for (const suffix of SQLITE_SIDECAR_SUFFIXES) {
		const sidecar = sqliteSidecarPath(targetPath, suffix);
		rmSync(sidecar, { force: true });
		const backupSidecar = emergencyBackupSidecarPath(targetPath, suffix);
		if (existsSync(backupSidecar)) copyFileSync(backupSidecar, sidecar);
	}
}

function writeRestoreCandidate(uncompressed: Buffer, tempPath: string): void {
	if (isSqliteFile(uncompressed)) {
		writeFileSync(tempPath, uncompressed, { flag: "wx" });
		return;
	}
	writeGzippedSqlDump(uncompressed.toString("utf8"), tempPath);
}

export function restoreBackup(backupPath: string, dbPath?: string | null): RestoreResult {
	const targetPath = dbPath ?? getDefaultPaths().dbPath;
	if (!existsSync(backupPath)) throw new FileNotFoundError(`Backup not found: ${backupPath}`);

	mkdirSync(dirname(targetPath), { recursive: true });

	const uncompressed = gunzipSync(readFileSync(backupPath));
	const tempPath = restoreTempPath(targetPath);
	let replacedTarget = false;
	try {
		writeRestoreCandidate(uncompressed, tempPath);
		if (!verifyIntegrity(tempPath)) throw new Error(`Backup failed integrity check: ${backupPath}`);
		snapshotCurrentDatabase(targetPath);
		renameSync(tempPath, targetPath);
		replacedTarget = true;
		removeSqliteSidecars(targetPath);
		const integrity = verifyIntegrity(targetPath);
		if (!integrity) throw new Error(`Restored database failed integrity check: ${backupPath}`);
		return {
			restored: true,
			backup_used: backupPath,

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the backup path exists (ls the backup directory) and pass the correct path to restoreBackup
  2. Create a fresh backup with createBackup first if none exists
  3. Check rotation/cleanup configuration so needed backups aren't deleted

Example fix

// before
restoreBackup("/backups/memory-2026-08-31.gz"); // never created
// after
const backupPath = "/backups/memory-2026-08-31.gz";
if (!existsSync(backupPath)) {
	({ backupPath } = createBackup(dbPath, "/backups"));
}
restoreBackup(backupPath);
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync } from "node:fs";
function safeRestore(backupPath, dbPath) {
	if (!existsSync(backupPath)) throw new Error(`backup missing: ${backupPath}`);
	return restoreBackup(backupPath, dbPath);
}

Try / catch

try {
	restoreBackup(backupPath, dbPath);
} catch (err) {
	if (err.name === "FileNotFoundError" && err.message.startsWith("Backup not found")) {
		const available = readdirSync(backupDir);
		throw new Error(`backup not found; available: ${available.join(", ")}`, { cause: err });
	}
	throw err;
}

Prevention

When it happens

Trigger: restoreBackup(path) with a typo'd path or a backup that was deleted/rotated away; pointing at a backup in another directory; running restore before any backup was created; the backup job failed earlier so the expected file never appeared.

Common situations: Cleanup jobs pruning old backups that are still referenced; path built from a timestamp string with a typo; cross-machine restore where the file wasn't transferred; expecting default backup dir contents on a fresh machine.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/bc74f16ff9ee1612. Report an issue: GitHub.