immich-app/immich · critical · Error

Invalid backup file format!

Error message

Invalid backup file format!

What it means

Thrown (plain Error) at the start of DatabaseBackupService.restoreDatabaseBackup when isValidDatabaseBackupName(filename) fails. Unlike the upload/download/delete variants (which are 400s), this restore-time check is a hard internal error: you cannot restore from a file whose name is not a recognized SQL/gzip dump.

Source

Thrown at server/src/services/database-backup.service.ts:361

    toDelete.push(...failedBackups);

    for (const file of toDelete) {
      await this.storageRepository.unlink(path.join(backupsFolder, file));
    }

    this.logger.debug(`Database Backup Cleanup Finished, deleted ${toDelete.length} backups`);
  }

  async restoreDatabaseBackup(
    filename: string,
    progressCb?: (action: 'backup' | 'restore' | 'migrations' | 'rollback', progress: number) => void,
  ): Promise<void> {
    this.logger.debug(`Database Restore Started`);

    let isComplete = false;
    try {
      if (!isValidDatabaseBackupName(filename)) {
        throw new Error('Invalid backup file format!');
      }

      const backupFilePath = path.join(StorageCore.getBaseFolder(StorageFolder.Backups), filename);
      await this.storageRepository.stat(backupFilePath); // => check file exists

      let isPgClusterDump = false;
      const version = findDatabaseBackupVersion(filename);
      if (version && semver.satisfies(version, '<= 2.4')) {
        isPgClusterDump = true;
      }

      const { bin, args, databaseUsername, databasePassword, databaseMajorVersion } =
        await this.buildPostgresLaunchArguments('psql', {
          singleTransaction: !isPgClusterDump,
        });

      progressCb?.('backup', 0.05);

View on GitHub (pinned to 199723261c)

Solutions

  1. Restore only from files listed by the list-backups endpoint (already validated).
  2. Confirm the extension is '.sql' or '.sql.gz' and the name is ASCII-safe.
  3. Pass the basename only, not a path.

Example fix

// before
await backupService.restoreDatabaseBackup(input);

// after
const VALID = /^[\w\d.-]+\.sql(?:\.gz)?$/;
if (!VALID.test(input)) {
  throw new Error(`Cannot restore '${input}': not a valid backup filename.`);
}
await backupService.restoreDatabaseBackup(input);
Defensive patterns

Strategy: validation

Validate before calling

const VALID = /^[\w\d.-]+\.sql(?:\.gz)?$/;
if (!VALID.test(filename)) {
  throw new Error(`'${filename}' is not a valid backup filename.`);
}

Type guard

function isValidBackupName(name: string): boolean {
  return /^[\w\d.-]+\.sql(?:\.gz)?$/.test(name);
}

Prevention

When it happens

Trigger: Triggering a restore with a filename that does not match /^[\w\d.-]+\.sql(?:\.gz)?$/ — wrong extension, special characters, or a path separator.

Common situations: Selecting a non-backup file in a restore UI; passing a relative path instead of just a filename; filename case/extension mismatch.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/0f6358324769b132. Report an issue: GitHub.