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

  1. Inspect error.cause (and the preceding 'Unable to initialize reverse geocoding' log line) for the root reason.
  2. Re-run the geodata setup / ensure the geodata files are present and writable; let Immich re-download them if applicable.
  3. If a DB lock is stuck, investigate pg_locks / restart Postgres or clear the stale advisory lock.
  4. 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

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


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