paperclipai/paperclip · error

HEIF exceeds the attachment byte limit

Error message

HEIF exceeds the attachment byte limit

What it means

validateHeifDimensions first checks that the body is non-empty and not larger than MAX_ATTACHMENT_BYTES before parsing any boxes. It throws when the HEIF payload is zero-length or exceeds the configured attachment byte cap, so oversized files are rejected before any structure parsing or native decoding.

Solutions

  1. Check body.length in the caller and return a 413 Payload Too Large with the allowed size before invoking validation.
  2. Configure or verify MAX_ATTACHMENT_BYTES in attachment-types.js to match your gateway's body-size limits (e.g. express.json({limit})).
  3. Fix the upload path producing empty buffers — verify the client actually streamed the bytes and Content-Length matches.
  4. Compress/resize the HEIC client-side (e.g. sips -s formatOptions 50%) to fit under the byte cap.

Example fix

// before
router.post('/attachments', async (req, res) => {
  await validatePhotonImage(req.body, req.headers['content-type']);
});
// after
router.post('/attachments', async (req, res) => {
  if (!req.body?.length || req.body.length > MAX_ATTACHMENT_BYTES)
    return res.status(413).json({ error: 'attachment too large or empty' });
  await validatePhotonImage(req.body, req.headers['content-type']);
});
Defensive patterns

Strategy: validation

Validate before calling

import { MAX_ATTACHMENT_BYTES } from './attachment-types.js';
export function isAttachmentWithinByteLimit(body?: Buffer | null): boolean {
  return !!body && body.length > 0 && body.length <= MAX_ATTACHMENT_BYTES;
}

Type guard

function hasBytes(body: Buffer | undefined | null): body is Buffer {
  return Buffer.isBuffer(body) && body.length > 0;
}

Try / catch

try {
  await validatePhotonImage(body, contentType);
} catch (err) {
  if (err instanceof Error && err.message === 'HEIF exceeds the attachment byte limit') {
    return respond(413, `attachment must be between 1 byte and ${MAX_ATTACHMENT_BYTES} bytes`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling validatePhotonImage with a HEIF content type and a Buffer that is empty (0 bytes) or whose length exceeds MAX_ATTACHMENT_BYTES (from server/src/attachment-types.js).

Common situations: Failed client uploads producing empty buffers (truncated request bodies, wrong Content-Length); users attaching 100MB+ HEIC video-like sequences; proxy/gateway limits stripping bodies.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

        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,
  contentType: string,
): Promise<void> {
  if (!contentType.startsWith("image/")) return;
  if (HEIF_CONTENT_TYPES.has(contentType)) return validateHeifDimensions(body);
  const metadata = await sharp(body, {
    limitInputPixels: MAX_PIXELS,
    failOn: "error",
  }).metadata();
  if (
    !metadata.width ||
    !metadata.height ||

View on GitHub (pinned to 3f1d897a7c)