{"record":{"id":"2a540cd1997c8a34","repo":"immich-app/immich","slug":"asset-not-found-2a540c","errorCode":null,"errorMessage":"Asset not found","messagePattern":"Asset not found","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"server/src/services/person.service.ts","lineNumber":620,"sourceCode":"\n  private findOrFail(id: string) {\n    return findOrFail(() => this.personRepository.getById(id), 'Person');\n  }\n\n  // TODO return a asset face response\n  async createFace(auth: AuthDto, dto: AssetFaceCreateDto): Promise<void> {\n    await Promise.all([\n      this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [dto.assetId] }),\n      this.requireAccess({ auth, permission: Permission.PersonRead, ids: [dto.personId] }),\n    ]);\n\n    const [asset, person] = await Promise.all([\n      this.assetRepository.getById(dto.assetId, { edits: true, exifInfo: true }),\n      this.findOrFail(dto.personId),\n    ]);\n\n    if (!asset) {\n      throw new NotFoundException('Asset not found');\n    }\n\n    const edits = asset.edits || [];\n\n    let topLeft: Point = { x: dto.x, y: dto.y };\n    let bottomRight: Point = { x: dto.x + dto.width, y: dto.y + dto.height };\n\n    // the coordinates received from the client are based on the edited preview image\n    // we need to convert them to the coordinate space of the original unedited image\n    if (edits.length > 0) {\n      if (!asset.width || !asset.height || !asset.exifInfo?.exifImageWidth || !asset.exifInfo?.exifImageHeight) {\n        throw new BadRequestException('Asset does not have valid dimensions');\n      }\n\n      // convert from preview to full dimensions\n      const scaleFactor = asset.width / dto.imageWidth;\n      topLeft = { x: topLeft.x * scaleFactor, y: topLeft.y * scaleFactor };\n      bottomRight = { x: bottomRight.x * scaleFactor, y: bottomRight.y * scaleFactor };","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/person.service.ts#L602-L638","documentation":"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.","triggerScenarios":"POST /people/{personId}/faces (or the face-create endpoint) with an assetId that was deleted, never existed, or is malformed.","commonSituations":"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.","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."],"exampleFix":"// before\nif (!asset) {\n  throw new NotFoundException('Asset not found');\n}\n\n// after (the access check should have caught this; surface the precise reason)\nif (!asset) {\n  throw new NotFoundException(`Asset ${dto.assetId} not found`);\n}","handlingStrategy":"validation","validationCode":"// Confirm the asset exists before tagging a face on it.\nconst asset = await assetService.getById(auth, dto.assetId).catch(() => null);\nif (!asset) {\n  throw new Error(`Refusing face-tag: asset ${dto.assetId} does not exist`);\n}\nawait personService.createFace(auth, dto);","typeGuard":"const isAsset = (a: AssetResponseDto | null | undefined): a is AssetResponseDto =>\n  !!a && typeof a.id === 'string';","tryCatchPattern":"try {\n  await personService.createFace(auth, dto);\n} catch (e) {\n  if (e instanceof NotFoundException && /Asset/.test(e.message)) {\n    // drop the local face-tag attempt and refresh the asset list\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["person","face","asset","not-found","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}