immich-app/immich · error

Face not found

Error message

Face ${id} not found

What it means

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.

Solutions

  1. Confirm the face id still exists (person.faces / asset joins) — if it was deleted, the Failed status is expected and can be ignored.
  2. Clear stale queued ML jobs (drain/restart the ML job queue) so dead face ids are not reprocessed.
  3. If assets are orphaned, repair the database so faces reference valid assets (re-run smart-search/facial-recognition pipelines from the admin UI).
  4. Check sourceType: faces not from MachineLearning source are skipped intentionally — verify the face was produced by the ML pipeline.
Defensive patterns

Strategy: validation

Validate before calling

// before enqueuing recognition for a face id
const face = await personRepository.getFaceForFacialRecognitionJob(id);
if (!face || !face.asset) {
  logger.warn(`Face ${id} not found; skipping enqueue`);
} else if (face.sourceType !== SourceType.MachineLearning) {
  logger.warn(`Face ${id} has non-ML source; skipping`);
} else {
  await queue.add({ name: JobName.FacialRecognition, data: { faceId: id } });
}

Type guard

const isRecognizableFace = (
  f: Awaited<ReturnType<typeof personRepository.getFaceForFacialRecognitionJob>>,
): f is FaceWithAsset => !!f && !!f.asset;

Try / catch

const status = await handleRecognizeFaces({ id });
if (status === JobStatus.Failed) {
  logger.warn(`Face ${id} recognition failed (face or asset missing); dropping job`);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15). Data as JSON: /api/errors/5b3e348c3ad9d45e. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/person.service.ts:490

        batch.map((face) => ({ name: JobName.FacialRecognition, data: { id: face.id, deferred: false } })),
      );
    }

    await this.systemMetadataRepository.set(SystemMetadataKey.FacialRecognitionState, { lastRun });

    return JobStatus.Success;
  }

  @OnJob({ name: JobName.FacialRecognition, queue: QueueName.FacialRecognition })
  async handleRecognizeFaces({ id, deferred }: JobOf<JobName.FacialRecognition>): Promise<JobStatus> {
    const { machineLearning } = await this.getConfig({ withCache: true });
    if (!isFacialRecognitionEnabled(machineLearning)) {
      return JobStatus.Skipped;
    }

    const face = await this.personRepository.getFaceForFacialRecognitionJob(id);
    if (!face || !face.asset) {
      this.logger.warn(`Face ${id} not found`);
      return JobStatus.Failed;
    }

    if (face.sourceType !== SourceType.MachineLearning) {
      this.logger.warn(`Skipping face ${id} due to source ${face.sourceType}`);
      return JobStatus.Skipped;
    }

    if (!face.faceSearch?.embedding) {
      this.logger.warn(`Face ${id} does not have an embedding`);
      return JobStatus.Failed;
    }

    if (face.personGroupId) {
      this.logger.debug(`Face ${id} already has a person assigned`);
      return JobStatus.Skipped;
    }

View on GitHub (pinned to f48d4b3321)