{"record":{"id":"71e91b09724e733d","repo":"immich-app/immich","slug":"metadata-service-init-failed","errorCode":null,"errorMessage":"Metadata service init failed","messagePattern":"Metadata service init failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"server/src/services/metadata.service.ts","lineNumber":173,"sourceCode":"  }\n\n  @OnEvent({ name: 'ConfigUpdate', workers: [ImmichWorker.Microservices], server: true })\n  onConfigUpdate({ newConfig }: ArgOf<'ConfigUpdate'>) {\n    this.metadataRepository.setMaxConcurrency(newConfig.job.metadataExtraction.concurrency);\n  }\n\n  private async init() {\n    this.logger.log('Initializing metadata service');\n\n    try {\n      await this.jobRepository.pause(QueueName.MetadataExtraction);\n      await this.databaseRepository.withLock(DatabaseLock.GeodataImport, () => this.mapRepository.init());\n      await this.jobRepository.resume(QueueName.MetadataExtraction);\n\n      this.logger.log(`Initialized local reverse geocoder`);\n    } catch (error: Error | any) {\n      this.logger.error(`Unable to initialize reverse geocoding: ${error}`, error?.stack);\n      throw new Error('Metadata service init failed', { cause: error });\n    }\n  }\n\n  private async linkLivePhotos(\n    asset: { id: string; type: AssetType; ownerId: string; libraryId: string | null },\n    exifInfo: Insertable<AssetExifTable>,\n  ): Promise<void> {\n    if (!exifInfo.livePhotoCID) {\n      return;\n    }\n\n    const otherType = asset.type === AssetType.Video ? AssetType.Image : AssetType.Video;\n    const match = await this.assetRepository.findLivePhotoMatch({\n      livePhotoCID: exifInfo.livePhotoCID,\n      ownerId: asset.ownerId,\n      libraryId: asset.libraryId,\n      otherAssetId: asset.id,\n      type: otherType,","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/metadata.service.ts#L155-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-flight: confirm geodata is present and DB is reachable\nimport { promises as fs } from 'fs';\nconst geodataExists = await fs.stat('/path/to/geodata/cities500.txt').then(() => true).catch(() => false);\nif (!geodataExists) throw new Error('Geodata missing - run the geodata download step');","typeGuard":null,"tryCatchPattern":"try {\n  await app.start(); // triggers MetadataService.init\n} catch (e) {\n  if (/Metadata service init failed/.test(String(e?.message))) {\n    logger.fatal({ cause: e?.cause }, 'Reverse geocoder init failed - check geodata and DB lock');\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["metadata","geodata","bootstrap","reverse-geocoding","immich","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}