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
- 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.
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
- 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.
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
- Not Found
- Invalid assetId for feature face or asset is offline
- Cannot merge a person into themselves
- Asset not found
- API Key not found
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/38c6a9e898e97490.
Report an issue: GitHub.