{"record":{"id":"5b3e348c3ad9d45e","repo":"immich-app/immich","slug":"face-id-not-found","errorCode":null,"errorMessage":"Face ${id} not found","messagePattern":"Face (.+?) not found","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/person.service.ts","lineNumber":490,"sourceCode":"        batch.map((face) => ({ name: JobName.FacialRecognition, data: { id: face.id, deferred: false } })),\n      );\n    }\n\n    await this.systemMetadataRepository.set(SystemMetadataKey.FacialRecognitionState, { lastRun });\n\n    return JobStatus.Success;\n  }\n\n  @OnJob({ name: JobName.FacialRecognition, queue: QueueName.FacialRecognition })\n  async handleRecognizeFaces({ id, deferred }: JobOf<JobName.FacialRecognition>): Promise<JobStatus> {\n    const { machineLearning } = await this.getConfig({ withCache: true });\n    if (!isFacialRecognitionEnabled(machineLearning)) {\n      return JobStatus.Skipped;\n    }\n\n    const face = await this.personRepository.getFaceForFacialRecognitionJob(id);\n    if (!face || !face.asset) {\n      this.logger.warn(`Face ${id} not found`);\n      return JobStatus.Failed;\n    }\n\n    if (face.sourceType !== SourceType.MachineLearning) {\n      this.logger.warn(`Skipping face ${id} due to source ${face.sourceType}`);\n      return JobStatus.Skipped;\n    }\n\n    if (!face.faceSearch?.embedding) {\n      this.logger.warn(`Face ${id} does not have an embedding`);\n      return JobStatus.Failed;\n    }\n\n    if (face.personGroupId) {\n      this.logger.debug(`Face ${id} already has a person assigned`);\n      return JobStatus.Skipped;\n    }\n","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/person.service.ts#L472-L508","documentation":"handleRecognizeFaces is a machine-learning job entry point that runs facial recognition for a single face id. Before doing so it loads the face together with its asset via personRepository.getFaceForFacialRecognitionJob(id). If no face row exists, or the face exists but its asset row is missing (orphaned), the job logs this warning and returns JobStatus.Failed rather than throwing.","triggerScenarios":"A facial-recognition job enqueued with a face id whose row was deleted between enqueue and processing, or whose joined asset row is missing; queueing recognition for faces created by a since-rolled-back or partially-failed ML sync.","commonSituations":"Faces removed by a 'reset recognized faces' operation while jobs were still queued; database restores or migrations that left faces without matching assets; stale queued jobs after a library/asset deletion.","solutions":["Confirm the face id still exists (person.faces / asset joins) — if it was deleted, the Failed status is expected and can be ignored.","Clear stale queued ML jobs (drain/restart the ML job queue) so dead face ids are not reprocessed.","If assets are orphaned, repair the database so faces reference valid assets (re-run smart-search/facial-recognition pipelines from the admin UI).","Check sourceType: faces not from MachineLearning source are skipped intentionally — verify the face was produced by the ML pipeline."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before enqueuing recognition for a face id\nconst face = await personRepository.getFaceForFacialRecognitionJob(id);\nif (!face || !face.asset) {\n  logger.warn(`Face ${id} not found; skipping enqueue`);\n} else if (face.sourceType !== SourceType.MachineLearning) {\n  logger.warn(`Face ${id} has non-ML source; skipping`);\n} else {\n  await queue.add({ name: JobName.FacialRecognition, data: { faceId: id } });\n}","typeGuard":"const isRecognizableFace = (\n  f: Awaited<ReturnType<typeof personRepository.getFaceForFacialRecognitionJob>>,\n): f is FaceWithAsset => !!f && !!f.asset;","tryCatchPattern":"const status = await handleRecognizeFaces({ id });\nif (status === JobStatus.Failed) {\n  logger.warn(`Face ${id} recognition failed (face or asset missing); dropping job`);\n}","preventionTips":["Drain the ML job queue after bulk face deletions or 'reset faces' operations.","After restoring a database, re-run facial recognition instead of replaying old queued jobs.","Watch for orphaned faces (asset deleted) and clean them up with DB consistency checks.","Only enqueue faces with sourceType MachineLearning."],"tags":["immich","facial-recognition","job","entity-not-found"],"backgroundTag":"entity-not-found","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}