{"record":{"id":"38c6a9e898e97490","repo":"immich-app/immich","slug":"person-not-found","errorCode":null,"errorMessage":"Person not found","messagePattern":"Person not found","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"warning","filePath":"server/src/services/person.service.ts","lineNumber":61,"sourceCode":"import { ImmichFileResponse } from 'src/utils/file';\nimport { mimeTypes } from 'src/utils/mime-types';\nimport { batched, findOrFail, isFacialRecognitionEnabled } from 'src/utils/misc';\nimport { Point, transformPoints } from 'src/utils/transform';\n\n@Injectable()\nexport class PersonService extends BaseService {\n  async getAll(auth: AuthDto, dto: PersonSearchDto): Promise<PeopleResponseDto> {\n    const { withHidden = false, closestAssetId, closestPersonId, page, size } = dto;\n    let closestFaceAssetId = closestAssetId;\n    const pagination = {\n      take: size,\n      skip: (page - 1) * size,\n    };\n\n    if (closestPersonId) {\n      const person = await this.personRepository.getById(closestPersonId);\n      if (!person?.faceAssetId) {\n        throw new NotFoundException('Person not found');\n      }\n      closestFaceAssetId = person.faceAssetId;\n    }\n    const { items, hasNextPage } = await this.personRepository.getAllForUser(pagination, auth.user.id, {\n      withHidden,\n      closestFaceAssetId,\n    });\n    const { total, hidden } = await this.personRepository.getNumberOfPeople(auth.user.id);\n\n    return {\n      people: items.map((person) => mapPerson(person)),\n      hasNextPage,\n      total,\n      hidden,\n    };\n  }\n\n  async reassignFaces(auth: AuthDto, personId: string, dto: AssetFaceUpdateDto): Promise<PersonResponseDto[]> {","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/person.service.ts#L43-L79","documentation":"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.","triggerScenarios":"GET /people with closestPersonId pointing at a person that does not exist, belongs to another user, was deleted, or has no featured face asset.","commonSituations":"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.","solutions":["Drop the closestPersonId query param when the person no longer exists.","Re-fetch the people list to obtain fresh person ids after facial recognition changes.","Set a feature face for the person (PUT /people/{id} with featureFaceAssetId) before using it as closestPersonId."],"exampleFix":"// before\nif (closestPersonId) {\n  const person = await this.personRepository.getById(closestPersonId);\n  if (!person?.faceAssetId) {\n    throw new NotFoundException('Person not found');\n  }\n  closestFaceAssetId = person.faceAssetId;\n}\n\n// after (return an empty ordering instead of 404 when the anchor is gone)\nif (closestPersonId) {\n  const person = await this.personRepository.getById(closestPersonId);\n  closestFaceAssetId = person?.faceAssetId ?? undefined;\n}","handlingStrategy":"validation","validationCode":"// Before using a closestPersonId, verify it still exists and has a feature face.\nconst person = await personService.getById(auth, closestPersonId).catch(() => null);\nif (!person?.faceAssetId) {\n  // drop the param and list people in default order\n  delete dto.closestPersonId;\n}","typeGuard":"const hasFeatureFace = (p: PersonResponseDto | null | undefined): p is PersonResponseDto =>\n  !!p && typeof p.faceAssetId === 'string';","tryCatchPattern":"try {\n  await personService.getAll(auth, dto);\n} catch (e) {\n  if (e instanceof NotFoundException && dto.closestPersonId) {\n    // retry without the stale anchor\n    delete dto.closestPersonId;\n    return personService.getAll(auth, dto);\n  }\n  throw e;\n}","preventionTips":["Re-fetch person ids after facial recognition reruns.","Drop closestPersonId from cached query state when the people list is refreshed.","Set a feature face on persons you intend to use as proximity anchors."],"tags":["person","facial-recognition","not-found","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}