immich-app/immich · warning · BadRequestException
Crop parameters are out of bounds
Error message
Crop parameters are out of bounds
What it means
Thrown by editAsset inside the crop block when the requested crop rectangle exceeds the asset bounds: crop.parameters.x + width > assetWidth OR y + height > assetHeight. The server validates geometry against the asset's real dimensions and rejects oversized/off-edge rectangles with 400 BadRequest.
Source
Thrown at server/src/services/asset.service.ts:578
}
const edits = dto.edits as AssetEditActionItem[];
const crop = edits.find((e) => e.action === AssetEditAction.Crop);
if (crop) {
if (edits[0].action !== AssetEditAction.Crop) {
throw new BadRequestException('Crop action must be the first edit action');
}
// check that crop parameters will not go out of bounds
const { width: assetWidth, height: assetHeight } = getDimensions(asset);
if (!assetWidth || !assetHeight) {
throw new BadRequestException('Asset dimensions are not available for editing');
}
const { x, y, width, height } = crop.parameters;
if (x + width > assetWidth || y + height > assetHeight) {
throw new BadRequestException('Crop parameters are out of bounds');
}
}
const newEdits = await this.assetEditRepository.replaceAll(id, edits);
await this.jobRepository.queue({ name: JobName.AssetEditThumbnailGeneration, data: { id } });
// Return the asset and its applied edits
return {
assetId: id,
edits: newEdits,
};
}
async removeAssetEdits(auth: AuthDto, id: string): Promise<void> {
await this.requireAccess({ auth, permission: Permission.AssetEditDelete, ids: [id] });
const asset = await this.assetRepository.getById(id);
if (!asset) {View on GitHub (pinned to 199723261c)
Solutions
- Clamp the crop rectangle to [0,0,assetWidth,assetHeight] before submitting.
- Compute crop coordinates from the same width/height the server reports (asset.exifInfo width/height).
- Subtract a 1px safety margin to avoid off-by-one overshoot.
Example fix
// before
const crop = { x, y, width, height };
// after
const x = Math.max(0, Math.min(crop.x, assetWidth - 1));
const y = Math.max(0, Math.min(crop.y, assetHeight - 1));
const width = Math.min(crop.width, assetWidth - x);
const height = Math.min(crop.height, assetHeight - y);
const crop = { x, y, width, height }; Defensive patterns
Strategy: validation
Validate before calling
// Clamp the crop rect to the asset's real dimensions before submit.
const assetWidth = asset.exifInfo?.width;
const assetHeight = asset.exifInfo?.height;
const x = Math.max(0, Math.min(crop.x, assetWidth - 1));
const y = Math.max(0, Math.min(crop.y, assetHeight - 1));
const width = Math.min(crop.width, assetWidth - x);
const height = Math.min(crop.height, assetHeight - y);
if (x + width > assetWidth || y + height > assetHeight) {
throw new Error('Crop rect still out of bounds after clamping');
}
await api.put(`/assets/${id}/edits`, { edits: [{ action: 'Crop', parameters: { x, y, width, height } }, ...rest] }); Try / catch
try {
await api.put(`/assets/${id}/edits`, { edits });
} catch (e) {
if (e.response?.status === 400 && /out of bounds/i.test(e.response?.data?.message)) {
edits = clampCropToAsset(edits, asset);
await api.put(`/assets/${id}/edits`, { edits });
} else throw e;
} Prevention
- Compute crop coordinates in the same coordinate space the server uses (full-res dimensions).
- Clamp x+width <= assetWidth and y+height <= assetHeight before submitting.
- Leave a 1px safety margin to absorb rounding.
When it happens
Trigger: PUT /assets/{id}/edits with a Crop whose x+width or y+height exceeds the asset's pixel width/height; crop rectangle built against a downscaled preview but submitted against full-resolution dimensions; negative x/y making the rect extend past the edge.
Common situations: Client computes the crop on a preview image but sends coordinates in a different scale than the server's stored dimensions; rounding errors that push width/height one pixel over; stale asset dimensions after a re-encode.
Related errors
- Crop action must be the first edit action
- Only images can be edited
- Editing live photos is not supported
- Editing panorama images is not supported
- Editing GIF images is not supported
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/7c0aa5f328a0e063.
Report an issue: GitHub.