immich-app/immich · error · BadRequestException

Asset does not have valid dimensions

Error message

Asset does not have valid dimensions

What it means

When the target asset has edits (crops/rotations from the edit stack), the service must convert client coordinates from the edited preview space to the original image space. That conversion requires asset.width/height and exifInfo.exifImageWidth/exifImageHeight; if any is missing or zero, it throws BadRequestException('Asset does not have valid dimensions') instead of computing a bogus region.

Solutions

  1. Remove the asset's edits (revert to original) before updating the face region, so no coordinate conversion is needed.
  2. Re-upload or re-process the asset so width/height and EXIF dimensions are populated (run metadata extraction job).
  3. Verify the file's EXIF; inject missing dimensions via exiftool if the source lacks them.
  4. Retry after the asset finishes processing (check asset.status is ready and exifInfo present).

Example fix

// before
await updateFaceRegion({ assetId, x, y, width, height, imageWidth }); // asset has edits but no EXIF dims
// after
await api.assetsApi.replaceAsset({ id: assetId, ... }); // or revert edits first
await updateFaceRegion({ assetId, x, y, width, height, imageWidth });
Defensive patterns

Strategy: fallback

Validate before calling

const asset = await api.assetsApi.getAssetInfo({ id: assetId });
const hasEdits = (asset.edits?.length ?? 0) > 0;
const dimsOk = !hasEdits || (asset.exifInfo?.exifImageWidth && asset.exifInfo?.exifImageHeight);
if (!dimsOk) throw new Error('Asset lacks EXIF dimensions required for edited-coordinate conversion');

Type guard

function hasValidDimensions(asset) {
  return Boolean(asset.width && asset.height && asset.exifInfo?.exifImageWidth && asset.exifInfo?.exifImageHeight);
}

Try / catch

try {
  await updateFaceRegion(dto);
} catch (e) {
  if (e.status === 400 && String(e.message).includes('valid dimensions')) {
    // fall back to reverting edits or re-running the metadata extraction job
  } else throw e;
}

Prevention

When it happens

Trigger: Updating a face region on an asset that has edits but lacks valid width/height metadata or EXIF image dimensions (missing EXIF, dimension-less sidecar, corrupted metadata, or media not yet fully processed).

Common situations: Screenshots or images stripped of EXIF that were then edited, assets mid-ingestion where dimensions aren't computed yet, corrupted metadata after restore, or third-party uploads without dimensions.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15). Data as JSON: /api/errors/0e6a3433e18d9b55. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/person.service.ts:701

    const [asset, person] = await Promise.all([
      this.assetRepository.getById(dto.assetId, { edits: true, exifInfo: true }),
      this.findOrFail(auth, 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 };

      const [invertedTopLeft, invertedBottomRight] = transformPoints(
        [topLeft, bottomRight],
        edits,
        { width: asset.width, height: asset.height },
        { inverse: true },
      ).points;

      // make sure topLeft is top-left and bottomRight is bottom-right
      topLeft = {
        x: Math.min(invertedTopLeft.x, invertedBottomRight.x),
        y: Math.min(invertedTopLeft.y, invertedBottomRight.y),

View on GitHub (pinned to f48d4b3321)