{"record":{"id":"019628975cfc3f82","repo":"immich-app/immich","slug":"server-health-check-failed-no-admin-exists","errorCode":null,"errorMessage":"Server health check failed, no admin exists.","messagePattern":"Server health check failed, no admin exists\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"server/src/services/database-backup.service.ts","lineNumber":420,"sourceCode":"      const [progressSource, progressSink] = createSqlProgressStreams((progress) => {\n        if (isComplete) {\n          return;\n        }\n\n        this.logger.log(`Restore progress ~ ${(progress * 100).toFixed(2)}%`);\n        progressCb?.('restore', progress);\n      });\n\n      await pipeline(sqlStream, createSqlOwnerTransformStream(databaseUsername), progressSource, psql, progressSink);\n\n      try {\n        progressCb?.('migrations', 0.9);\n\n        await this.databaseRepository.runMigrations();\n\n        const hasAdmin = await this.userRepository.hasAdmin();\n        if (!hasAdmin) {\n          throw new Error('Server health check failed, no admin exists.');\n        }\n\n        await this.maintenanceHealthRepository.checkApiHealth();\n      } catch (error) {\n        progressCb?.('rollback', 0);\n\n        const fileStream = this.storageRepository.createPlainReadStream(restorePointFilePath);\n        const gunzip = this.storageRepository.createGunzip();\n        fileStream.pipe(gunzip);\n        inputStream = gunzip;\n\n        const sqlStream = Readable.from(sqlRollback(inputStream, databaseUsername));\n        const psql = this.processRepository.spawnDuplexStream(bin, args, {\n          env: {\n            PATH: process.env.PATH,\n            PGPASSWORD: databasePassword,\n          },\n        });","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/database-backup.service.ts#L402-L438","documentation":"Thrown (plain Error) inside DatabaseBackupService.restoreDatabaseBackup after migrations run. As a post-restore health check it asserts userRepository.hasAdmin() is true; an empty (no-admin) restored DB means the restore did not yield a usable system, so it triggers the rollback path (restoring the pre-restore snapshot).","triggerScenarios":"Restoring a backup that contains no admin user — e.g. a partial/empty dump, a dump of the wrong database, or a backup taken before any admin existed.","commonSituations":"Restoring a dump from a non-Immich or empty schema; restoring a backup of a fresh DB that never had an admin; the migrations ran but the users table is empty.","solutions":["Restore from a known-good backup that was taken from a fully set-up instance (with an admin user).","If no such backup exists, start fresh, complete onboarding to create an admin, then take a backup.","Inspect the dump before restoring to confirm the users table contains an isAdmin=true row."],"exampleFix":"// before\nawait backupService.restoreDatabaseBackup(file);\n\n// after\n// Validate the source dump is non-empty and contains an admin before attempting restore.\nconst preview = await inspectDump(path.join(BACKUP_DIR, file));\nif (!preview.hasAdminUser) {\n  throw new Error('Refusing to restore: backup contains no admin user; pick a complete backup.');\n}\nawait backupService.restoreDatabaseBackup(file);","handlingStrategy":"fallback","validationCode":"// Pre-flight: confirm the dump contains an admin before restoring.\nconst preview = await inspectDump(path.join(BACKUP_DIR, filename));\nif (!preview.hasAdminUser) {\n  throw new Error('Backup has no admin user; refusing restore.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await backupService.restoreDatabaseBackup(filename, onProgress);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('no admin exists')) {\n    // service already rolled back to the snapshot; surface a clear message\n    throw new Error('Restore failed: backup contained no admin; rolled back to previous state.');\n  }\n  throw e;\n}","preventionTips":["Only restore backups taken from a fully on boarded instance.","Inspect the dump's users table for an admin row before restoring.","Keep a pre-restore snapshot (the service does this) and verify rollback succeeds."],"tags":["database","backup","restore","admin","health-check"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}