immich-app/immich · critical · Error
Metadata service init failed
Error message
Metadata service init failed
What it means
MetadataService.init initializes the local reverse geocoder (mapRepository.init) and pauses/resumes the MetadataExtraction queue around it. If anything in that sequence throws (geodata import failure, map data missing/currupt, lock acquisition error), the catch wraps it into a new Error 'Metadata service init failed' with the original as cause. This is a bootstrap-time failure that prevents the metadata pipeline from starting.
Source
Thrown at server/src/services/metadata.service.ts:173
}
@OnEvent({ name: 'ConfigUpdate', workers: [ImmichWorker.Microservices], server: true })
onConfigUpdate({ newConfig }: ArgOf<'ConfigUpdate'>) {
this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);
}
private async init() {
this.logger.log('Initializing metadata service');
try {
await this.jobRepository.pause(QueueName.MetadataExtraction);
await this.databaseRepository.withLock(DatabaseLock.GeodataImport, () => this.mapRepository.init());
await this.jobRepository.resume(QueueName.MetadataExtraction);
this.logger.log(`Initialized local reverse geocoder`);
} catch (error: Error | any) {
this.logger.error(`Unable to initialize reverse geocoding: ${error}`, error?.stack);
throw new Error('Metadata service init failed', { cause: error });
}
}
private async linkLivePhotos(
asset: { id: string; type: AssetType; ownerId: string; libraryId: string | null },
exifInfo: Insertable<AssetExifTable>,
): Promise<void> {
if (!exifInfo.livePhotoCID) {
return;
}
const otherType = asset.type === AssetType.Video ? AssetType.Image : AssetType.Video;
const match = await this.assetRepository.findLivePhotoMatch({
livePhotoCID: exifInfo.livePhotoCID,
ownerId: asset.ownerId,
libraryId: asset.libraryId,
otherAssetId: asset.id,
type: otherType,View on GitHub (pinned to 199723261c)
Solutions
- Inspect error.cause (and the preceding 'Unable to initialize reverse geocoding' log line) for the root reason.
- Re-run the geodata setup / ensure the geodata files are present and writable; let Immich re-download them if applicable.
- If a DB lock is stuck, investigate pg_locks / restart Postgres or clear the stale advisory lock.
- Restart the Immich server once the underlying cause is fixed so init re-runs.
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: confirm geodata is present and DB is reachable
import { promises as fs } from 'fs';
const geodataExists = await fs.stat('/path/to/geodata/cities500.txt').then(() => true).catch(() => false);
if (!geodataExists) throw new Error('Geodata missing - run the geodata download step'); Try / catch
try {
await app.start(); // triggers MetadataService.init
} catch (e) {
if (/Metadata service init failed/.test(String(e?.message))) {
logger.fatal({ cause: e?.cause }, 'Reverse geocoder init failed - check geodata and DB lock');
process.exit(1);
}
throw e;
} Prevention
- Ensure geodata files are present and writable before first boot.
- Monitor disk space (geodata import needs room).
- Watch for stuck DatabaseLock.GeodataImport advisory locks after crashes.
When it happens
Trigger: AppBootstrap triggers MetadataService.init; mapRepository.init fails - e.g. geodata archive missing, cities500.txt unreadable, DB lock DatabaseLock.GeodataImport unavailable, or a network/filesystem error loading geodata.
Common situations: Missing or partially downloaded geodata files on first boot; disk full; DB lock held by a stale transaction; custom container image that omitted the geodata step.
Related errors
- Asset metadata is not yet ready for streaming
- Asset not found or metadata not yet ready for streaming
- Duplicate items are not allowed: "${key}"
- User already has a PIN code
- User does not have a PIN code
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/71e91b09724e733d.
Report an issue: GitHub.