garrytan/gstack · warning · StrictModeError
image exceeds ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)}MB
Error message
image exceeds ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)}MB cap: ${src} (${Math.round(stat.size / 1024 / 1024)}MB) What it means
StrictModeError thrown when a local image file is a regular file but its byte size exceeds MAX_IMAGE_BYTES. The cap bounds memory before any readFileSync, preventing a multi-GB image from exhausting memory. Non-strict mode warns and substitutes a placeholder; strict makes it fatal. The message shows both the cap (MB) and the actual size (MB).
Source
Thrown at make-pdf/src/diagram-prepass.ts:673
// Bound the read BEFORE reading: a markdown image pointing at a special
// file (fifo, device) would hang readFileSync, and a multi-GB file would
// exhaust memory before any policy ran.
let stat: fs.Stats;
try {
stat = fs.statSync(filePath);
} catch {
opts.warn(`image unreadable: ${src}`);
return buildMissingImagePlaceholder(src);
}
if (!stat.isFile()) {
const msg = `image is not a regular file: ${src}`;
if (opts.strict) throw new StrictModeError(msg);
opts.warn(msg);
return buildMissingImagePlaceholder(src);
}
if (stat.size > MAX_IMAGE_BYTES) {
const msg = `image exceeds ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)}MB cap: ${src} (${Math.round(stat.size / 1024 / 1024)}MB)`;
if (opts.strict) throw new StrictModeError(msg);
opts.warn(msg);
return buildMissingImagePlaceholder(src);
}
let buf = fs.readFileSync(filePath);
let dims = imageDims(buf);
let mime = dims?.mime ?? mimeFromExtension(filePath);
// Print-resolution normalization (D4): rasters only — SVG scales free.
if (dims && mime !== "image/svg+xml" && dims.width > maxPx) {
const tab = opts.getTab();
if (tab) {
try {
const dataUri = `data:${mime};base64,${buf.toString("base64")}`;
const scaled = tab.call("__downscaleRaster", dataUri, targetPx, mime);
const scaledB64 = scaled.replace(/^data:[^,]*,/, "");
opts.warn(
`downscaled ${path.basename(filePath)} ${dims.width}px → ${targetPx}px ` +View on GitHub (pinned to 94993f7401)
Solutions
- Downscale/compress the image below MAX_IMAGE_BYTES (e.g. `convert big.png -resize 50% big.png`).
- Reference a smaller, web-resolution variant of the image.
- Drop --strict to degrade to a warn + placeholder.
- If the large image is intentional and print resolution matters, raise MAX_IMAGE_BYTES in a fork after verifying available memory.
Example fix
# before $ make-pdf --strict report.md # image is 180MB Error: image exceeds 64MB cap: ./assets/poster.png (180MB) # after: downscale $ convert ./assets/poster.png -resize 25% ./assets/poster.png $ make-pdf --strict report.md
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
import path from 'node:path';
const MAX_IMAGE_BYTES = 64 * 1024 * 1024; // match the library constant
function findOversizedImages(markdown: string, inputDir: string): { src: string; size: number }[] {
const re = /!\[[^\]]*\]\((?!https?:|data:)([^)]+)\)/g;
const big: { src: string; size: number }[] = [];
let m: RegExpExecArray | null;
while ((m = re.exec(markdown))) {
const p = path.resolve(inputDir, decodeURIComponent(m[1]));
try { const s = fs.statSync(p); if (s.isFile() && s.size > MAX_IMAGE_BYTES) big.push({ src: m[1], size: s.size }); } catch {}
}
return big;
} Type guard
import { StrictModeError } from './diagram-prepass';
function isStrictModeError(e: unknown): e is StrictModeError {
return e instanceof StrictModeError;
} Prevention
- Compress assets before committing (`pngquant`, `cjpeg`, `convert -resize`).
- Reference web-resolution variants, not print masters, in markdown.
- Add a pre-commit size check on the assets folder.
- Raise MAX_IMAGE_BYTES in a fork only after confirming available memory.
When it happens
Trigger: inlineLocalImages() under --strict where fs.statSync(filePath).size > MAX_IMAGE_BYTES. Typical with a raw camera TIFF, an uncompressed scan, or a mistakenly-referenced video file with an image extension.
Common situations: A designer committed a 200MB PSD exported as PNG; a doc references a print-resolution 300DPI A0 poster; an asset混淆 with a .png-named data dump; a CI machine with limited RAM hitting OOM before this guard existed.
Related errors
- remote image blocked (offline posture): ${src} — re-run with
- image not found: ${src} (resolved to ${filePath})
- image resolves OUTSIDE the input directory: ${src} → ${realF
- image is not a regular file: ${src}
- Screenshot too large for --base64 (>10MB). Use disk path ins
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/5b0ca809dcc3af54.
Report an issue: GitHub.