immich-app/immich · error · NotFoundException
Asset not found
Error message
Asset not found
What it means
Thrown by PersonService.createFace when the asset referenced by dto.assetId does not resolve through assetRepository.getById (with edits + exifInfo). The permission checks for AssetUpdate and PersonRead already passed, so this is purely a data-existence failure. NotFoundException -> HTTP 404.
Source
Thrown at server/src/services/person.service.ts:620
private findOrFail(id: string) {
return findOrFail(() => this.personRepository.getById(id), 'Person');
}
// TODO return a asset face response
async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise<void> {
await Promise.all([
this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [dto.assetId] }),
this.requireAccess({ auth, permission: Permission.PersonRead, ids: [dto.personId] }),
]);
const [asset, person] = await Promise.all([
this.assetRepository.getById(dto.assetId, { edits: true, exifInfo: true }),
this.findOrFail(dto.personId),
]);
if (!asset) {
throw new NotFoundException('Asset not found');
}
const edits = asset.edits || [];
let topLeft: Point = { x: dto.x, y: dto.y };
let bottomRight: Point = { x: dto.x + dto.width, y: dto.y + dto.height };
// the coordinates received from the client are based on the edited preview image
// we need to convert them to the coordinate space of the original unedited image
if (edits.length > 0) {
if (!asset.width || !asset.height || !asset.exifInfo?.exifImageWidth || !asset.exifInfo?.exifImageHeight) {
throw new BadRequestException('Asset does not have valid dimensions');
}
// convert from preview to full dimensions
const scaleFactor = asset.width / dto.imageWidth;
topLeft = { x: topLeft.x * scaleFactor, y: topLeft.y * scaleFactor };
bottomRight = { x: bottomRight.x * scaleFactor, y: bottomRight.y * scaleFactor };View on GitHub (pinned to 199723261c)
Solutions
- Refresh the asset list before letting the user tag a face.
- Confirm dto.assetId is a valid, existing asset via GET /assets/{id} before submitting.
- Handle 404 by dropping the local face-tag attempt.
Example fix
// before
if (!asset) {
throw new NotFoundException('Asset not found');
}
// after (the access check should have caught this; surface the precise reason)
if (!asset) {
throw new NotFoundException(`Asset ${dto.assetId} not found`);
} Defensive patterns
Strategy: validation
Validate before calling
// Confirm the asset exists before tagging a face on it.
const asset = await assetService.getById(auth, dto.assetId).catch(() => null);
if (!asset) {
throw new Error(`Refusing face-tag: asset ${dto.assetId} does not exist`);
}
await personService.createFace(auth, dto); Type guard
const isAsset = (a: AssetResponseDto | null | undefined): a is AssetResponseDto => !!a && typeof a.id === 'string';
Try / catch
try {
await personService.createFace(auth, dto);
} catch (e) {
if (e instanceof NotFoundException && /Asset/.test(e.message)) {
// drop the local face-tag attempt and refresh the asset list
}
throw e;
} Prevention
- Refresh the asset list before letting the user tag faces.
- Validate assetId format (UUID) before submitting.
- Handle 404 by reverting the optimistic face-tag in the UI.
When it happens
Trigger: POST /people/{personId}/faces (or the face-create endpoint) with an assetId that was deleted, never existed, or is malformed.
Common situations: Stale asset id in client state after deletion; race between delete and face-create; passing an encoded vs decoded UUID; asset belongs to a different visibility tier.
Related errors
- Invalid assetId for feature face or asset is offline
- Asset does not have valid dimensions
- Asset media not found
- Asset not found or asset is not a video
- Asset not found
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/2a540cd1997c8a34.
Report an issue: GitHub.