paperclipai/paperclip · error

HEIF decoded image exceeds the pixel limit

Error message

HEIF decoded image exceeds the pixel limit

What it means

validateHeifDimensions walks the ISO-BMFF box structure of a HEIF file and reads each ispe (image spatial extents) box to get declared width/height before any native decoder runs. It throws this error when a single ispe box declares a zero dimension, a dimension above 16,384, or width*height above MAX_PIXELS (50,000,000). This is a decompression-bomb guard preventing huge allocations in the native HEIF converter.

Solutions

  1. Resize or re-export the image to at most 16,384x16,384 and under 50 megapixels (e.g. sips -Z 16384 or convert to JPEG) before uploading.
  2. If legitimate images are being rejected and the product allows it, raise MAX_PIXELS or the 16,384 per-dimension cap in server/src/services/photon/media.ts.
  3. Catch this specific Error message in ingestAttachments and reject the attachment with a clear user-facing 'image too large' error instead of a 500.
  4. Pre-probe client-side: read the HEIC's ispe/metadata in the browser or client and warn before upload.

Example fix

// before
const jpeg = await heifToJpeg(body, {quality:80});
// after
const metadata = await sharp(body, {limitInputPixels: MAX_PIXELS}).metadata();
if ((metadata.width ?? 0) * (metadata.height ?? 0) > MAX_PIXELS)
  throw new Error("HEIF decoded image exceeds the pixel limit");
const jpeg = await heifToJpeg(body, {quality:80});
Defensive patterns

Strategy: validation

Validate before calling

import sharp from 'sharp';
export async function isHeifWithinPixelLimit(body: Buffer): Promise<boolean> {
  if (!body.length) return false;
  const meta = await sharp(body, { limitInputPixels: 50_000_000 }).metadata().catch(() => null);
  if (!meta) return false; // HEIF: parse ispe boxes or pre-convert
  const w = meta.width ?? 0, h = meta.height ?? 0;
  return w > 0 && h > 0 && w <= 16_384 && h <= 16_384 && w * h <= 50_000_000;
}

Type guard

function hasSafeHeifDimensions(d: { width?: number; height?: number }): boolean {
  return typeof d.width === 'number' && typeof d.height === 'number' &&
    d.width > 0 && d.height > 0 &&
    d.width <= 16_384 && d.height <= 16_384 &&
    d.width * d.height <= 50_000_000;
}

Try / catch

try {
  await validatePhotonImage(body, contentType);
} catch (err) {
  if (err instanceof Error && err.message === 'HEIF decoded image exceeds the pixel limit') {
    return respond(413, 'image resolution exceeds the 50MP limit');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling validatePhotonImage or photonHeifPreview with a body whose content type is image/heic, image/heif, image/heic-sequence, or image/heif-sequence, where any ispe box declares width or height of 0 or >16384, or a single image over 50MP.

Common situations: Users upload high-resolution iPhone panorama/ProRAW HEIC exports (e.g. 48MP+ photos) or mislabeled files whose ispe metadata exceeds limits; also crafted bomb files attempting decoder DoS.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/4be5e6d29cac1d60. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/photon/media.ts:56

        throw new Error("HEIF box exceeds file bounds");
      const content = at + header;
      if (type === "ftyp") {
        if (size < header + 8) throw new Error("HEIF file type is missing");
        const brands = body.toString("ascii", content, at + size);
        branded = /heic|heix|hevc|hevx|mif1|msf1/.test(brands);
      } else if (type === "ispe") {
        if (size !== header + 12)
          throw new Error("Invalid HEIF image dimensions");
        const width = body.readUInt32BE(content + 4);
        const height = body.readUInt32BE(content + 8);
        if (
          !width ||
          !height ||
          width > 16_384 ||
          height > 16_384 ||
          width * height > MAX_PIXELS
        )
          throw new Error("HEIF decoded image exceeds the pixel limit");
        totalPixels += width * height;
        if (totalPixels > MAX_PIXELS * 3 || ++dimensions > 512)
          throw new Error("HEIF image collection exceeds the pixel limit");
      } else if (["meta", "iprp", "ipco"].includes(type)) {
        visit(content + (type === "meta" ? 4 : 0), at + size, depth + 1);
      }
      at += size;
    }
  };
  if (!body.length || body.length > MAX_ATTACHMENT_BYTES)
    throw new Error("HEIF exceeds the attachment byte limit");
  visit(0, body.length, 0);
  if (!branded || !dimensions)
    throw new Error("HEIF dimensions could not be verified");
}

export async function validatePhotonImage(
  body: Buffer,

View on GitHub (pinned to 3f1d897a7c)