{"record":{"id":"0e6a3433e18d9b55","repo":"immich-app/immich","slug":"asset-does-not-have-valid-dimensions","errorCode":null,"errorMessage":"Asset does not have valid dimensions","messagePattern":"Asset does not have valid dimensions","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/person.service.ts","lineNumber":632,"sourceCode":"    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 };\n\n      const [invertedTopLeft, invertedBottomRight] = transformPoints(\n        [topLeft, bottomRight],\n        edits,\n        { width: asset.width, height: asset.height },\n        { inverse: true },\n      ).points;\n\n      // make sure topLeft is top-left and bottomRight is bottom-right\n      topLeft = {\n        x: Math.min(invertedTopLeft.x, invertedBottomRight.x),\n        y: Math.min(invertedTopLeft.y, invertedBottomRight.y),","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/person.service.ts#L614-L650","documentation":"Thrown by PersonService.createFace when the asset has edits but is missing width/height on the asset or exifImageWidth/exifImageHeight on exifInfo. These dimensions are required to scale the client-supplied face box from preview space back to the original image space. BadRequestException -> HTTP 400.","triggerScenarios":"POST face-create for an asset that has been edited but whose metadata extraction never produced width/height or EXIF image dimensions (corrupted or stripped EXIF, metadata job that failed).","commonSituations":"Asset uploaded with stripped EXIF; metadata extraction job failed for that asset; a sidecar-less RAW file with no decoded dimensions; the asset was edited before dimension metadata was populated.","solutions":["Re-run Metadata Extraction (Administration > Jobs) for the asset so dimensions are populated.","Tag the face on the unedited version of the asset (no edits => no scaling required).","If EXIF is irrecoverable, set width/height manually via the metadata API before tagging."],"exampleFix":"// before\nif (!asset.width || !asset.height || !asset.exifInfo?.exifImageWidth || !asset.exifInfo?.exifImageHeight) {\n  throw new BadRequestException('Asset does not have valid dimensions');\n}\n\n// after (skip preview->original scaling only when dimensions are missing AND the box matches original space)\nconst canScale = asset.width && asset.height && asset.exifInfo?.exifImageWidth && asset.exifInfo?.exifImageHeight;\nif (edits.length > 0 && !canScale) {\n  throw new BadRequestException('Cannot map face box: asset dimensions missing. Re-run metadata extraction.');\n}","handlingStrategy":"validation","validationCode":"// Before face-tagging on an edited asset, confirm dimensions exist.\nconst asset = await assetService.getById(auth, dto.assetId);\nif (asset.edits?.length && (!asset.width || !asset.height || !asset.exifInfo?.exifImageWidth || !asset.exifInfo?.exifImageHeight)) {\n  // queue metadata re-extraction and abort\n  throw new Error('Asset missing dimensions; queue metadata extraction first');\n}","typeGuard":"const hasDimensionsForScale = (a: AssetResponseDto): boolean =>\n  !!a.width && !!a.height && !!a.exifInfo?.exifImageWidth && !!a.exifInfo?.exifImageHeight;","tryCatchPattern":"try {\n  await personService.createFace(auth, dto);\n} catch (e) {\n  if (e instanceof BadRequestException && /valid dimensions/i.test(e.message)) {\n    // trigger metadata re-extraction, then retry once\n    await jobRepository.queue({ name: JobName.MetadataExtraction, data: { id: dto.assetId } });\n  }\n  throw e;\n}","preventionTips":["Run Metadata Extraction before allowing face tagging on edited assets.","Skip edited assets whose EXIF was stripped.","Provide width/height via the metadata API if EXIF is irrecoverable."],"tags":["person","face","asset","metadata","exif","nestjs","validation"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}