immich-app/immich · warning · NotFoundException
Not Found
Error message
Not Found
What it means
Thrown by PersonService.getThumbnail when personRepository.getById(id) returns null or the person has no thumbnailPath. NotFoundException with no message -> HTTP 404 with body 'Not Found'. Used after the permission check passes, so it indicates the thumbnail file is missing even though the person is accessible.
Source
Thrown at server/src/services/person.service.ts:166
await this.jobRepository.queueAll(jobs);
}
async getById(auth: AuthDto, id: string): Promise<PersonResponseDto> {
await this.requireAccess({ auth, permission: Permission.PersonRead, ids: [id] });
return mapPerson(await this.findOrFail(id));
}
async getStatistics(auth: AuthDto, id: string): Promise<PersonStatisticsResponseDto> {
await this.requireAccess({ auth, permission: Permission.PersonRead, ids: [id] });
return this.personRepository.getStatistics(id);
}
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
await this.requireAccess({ auth, permission: Permission.PersonRead, ids: [id] });
const person = await this.personRepository.getById(id);
if (!person || !person.thumbnailPath) {
throw new NotFoundException();
}
return new ImmichFileResponse({
path: person.thumbnailPath,
contentType: mimeTypes.lookup(person.thumbnailPath),
cacheControl: CacheControl.PrivateWithoutCache,
});
}
async create(auth: AuthDto, dto: PersonCreateDto): Promise<PersonResponseDto> {
const person = await this.personRepository.create({
ownerId: auth.user.id,
name: dto.name,
birthDate: dto.birthDate,
isHidden: dto.isHidden,
isFavorite: dto.isFavorite,
color: dto.color,
});View on GitHub (pinned to 199723261c)
Solutions
- Re-run the thumbnail generation queue for faces (Administration > Jobs > Thumbnail Generation).
- Verify the storage volume is mounted and the thumbnail path on disk is readable.
- Provide a message to the NotFoundException so clients can distinguish 'no thumbnail' from 'no person'.
Example fix
// before
const person = await this.personRepository.getById(id);
if (!person || !person.thumbnailPath) {
throw new NotFoundException();
}
// after
const person = await this.personRepository.getById(id);
if (!person) {
throw new NotFoundException(`Person ${id} not found`);
}
if (!person.thumbnailPath) {
throw new NotFoundException(`Person ${id} has no thumbnail`);
} Defensive patterns
Strategy: validation
Validate before calling
// Before requesting the thumbnail, verify the person has one.
const person = await personService.getById(auth, id);
if (!person || !person.hasThumbnail) {
// render a placeholder avatar instead of calling the thumbnail endpoint
return PLACEHOLDER_AVATAR;
} Type guard
const hasThumbnail = (p: PersonResponseDto | null | undefined): boolean => !!p && (!!p.thumbnailPath || !!p.hasThumbnail);
Try / catch
try {
return await personService.getThumbnail(auth, id);
} catch (e) {
if (e instanceof NotFoundException) {
// 404 from this endpoint means no thumbnail file; fall back to placeholder
return PLACEHOLDER_AVATAR;
}
throw e;
} Prevention
- Run the Thumbnail Generation job after facial recognition changes.
- Verify storage mounts are present after migrations.
- Add a message to the NotFoundException to distinguish missing-person from missing-thumbnail.
When it happens
Trigger: GET /people/{id}/thumbnail for a person whose thumbnail was never generated, was deleted from disk, or whose thumbnailPath column is empty.
Common situations: Thumbnail generation job failed or was interrupted; storage path was migrated/cleaned; the person was created manually (no face) and never had a thumbnail; cold migration left files behind.
Related errors
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/fbeed7719c1b7764.
Report an issue: GitHub.