immich-app/immich · warning · NotFoundException

Person not found

Error message

Person not found

What it means

Thrown by PersonService.getAll (the people list endpoint) when dto.closestPersonId is set but personRepository.getById returns null OR the person has no faceAssetId. NotFoundException -> HTTP 404. The closestFaceAssetId is needed to sort people by proximity to a chosen person.

Source

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

import { ImmichFileResponse } from 'src/utils/file';
import { mimeTypes } from 'src/utils/mime-types';
import { batched, findOrFail, isFacialRecognitionEnabled } from 'src/utils/misc';
import { Point, transformPoints } from 'src/utils/transform';

@Injectable()
export class PersonService extends BaseService {
  async getAll(auth: AuthDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {
    const { withHidden = false, closestAssetId, closestPersonId, page, size } = dto;
    let closestFaceAssetId = closestAssetId;
    const pagination = {
      take: size,
      skip: (page - 1) * size,
    };

    if (closestPersonId) {
      const person = await this.personRepository.getById(closestPersonId);
      if (!person?.faceAssetId) {
        throw new NotFoundException('Person not found');
      }
      closestFaceAssetId = person.faceAssetId;
    }
    const { items, hasNextPage } = await this.personRepository.getAllForUser(pagination, auth.user.id, {
      withHidden,
      closestFaceAssetId,
    });
    const { total, hidden } = await this.personRepository.getNumberOfPeople(auth.user.id);

    return {
      people: items.map((person) => mapPerson(person)),
      hasNextPage,
      total,
      hidden,
    };
  }

  async reassignFaces(auth: AuthDto, personId: string, dto: AssetFaceUpdateDto): Promise<PersonResponseDto[]> {

View on GitHub (pinned to 199723261c)

Solutions

  1. Drop the closestPersonId query param when the person no longer exists.
  2. Re-fetch the people list to obtain fresh person ids after facial recognition changes.
  3. Set a feature face for the person (PUT /people/{id} with featureFaceAssetId) before using it as closestPersonId.

Example fix

// before
if (closestPersonId) {
  const person = await this.personRepository.getById(closestPersonId);
  if (!person?.faceAssetId) {
    throw new NotFoundException('Person not found');
  }
  closestFaceAssetId = person.faceAssetId;
}

// after (return an empty ordering instead of 404 when the anchor is gone)
if (closestPersonId) {
  const person = await this.personRepository.getById(closestPersonId);
  closestFaceAssetId = person?.faceAssetId ?? undefined;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before using a closestPersonId, verify it still exists and has a feature face.
const person = await personService.getById(auth, closestPersonId).catch(() => null);
if (!person?.faceAssetId) {
  // drop the param and list people in default order
  delete dto.closestPersonId;
}

Type guard

const hasFeatureFace = (p: PersonResponseDto | null | undefined): p is PersonResponseDto =>
  !!p && typeof p.faceAssetId === 'string';

Try / catch

try {
  await personService.getAll(auth, dto);
} catch (e) {
  if (e instanceof NotFoundException && dto.closestPersonId) {
    // retry without the stale anchor
    delete dto.closestPersonId;
    return personService.getAll(auth, dto);
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /people with closestPersonId pointing at a person that does not exist, belongs to another user, was deleted, or has no featured face asset.

Common situations: Stale person id in client state after re-running facial recognition; passing a person id from a different account; the person's feature face was cleared in an edit.

Related errors


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