garrytan/gstack · warning · StrictModeError

image is not a regular file: ${src}

Error message

image is not a regular file: ${src}

What it means

StrictModeError thrown when a local image filePath exists and stat succeeds but stat.isFile() is false — i.e. the path is a directory, fifo, character/block device, or socket. Reading such a path could hang readFileSync (fifo) or produce garbage, so the pre-pass guards it; strict makes it fatal, otherwise warn + placeholder.

Source

Thrown at make-pdf/src/diagram-prepass.ts:667

    if (!realFilePath.startsWith(inputRoot)) {
      const msg = `image resolves OUTSIDE the input directory: ${src} → ${realFilePath}`;
      if (opts.strict) throw new StrictModeError(msg + " — move it under the markdown's directory or drop --strict");
      opts.warn(msg);
    }

    // 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) {

View on GitHub (pinned to 94993f7401)

Solutions

  1. Point the markdown src at an actual image file, not a directory or special file.
  2. Remove any fifo/device node masquerading as an image in the assets folder.
  3. Drop --strict to degrade to a warn + placeholder if appropriate.
  4. Audit the assets directory for non-regular files (`find . -type p -o -type b -o -type c`).

Example fix

<!-- before: assets is a directory -->
![x](./assets)

<!-- after -->
![x](./assets/logo.png)
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
import path from 'node:path';

function findNonRegularImages(markdown: string, inputDir: string): string[] {
  const re = /!\[[^\]]*\]\((?!https?:|data:)([^)]+)\)/g;
  const bad: string[] = [];
  let m: RegExpExecArray | null;
  while ((m = re.exec(markdown))) {
    const p = path.resolve(inputDir, decodeURIComponent(m[1]));
    try { if (!fs.statSync(p).isFile()) bad.push(m[1]); } catch { /* missing handled elsewhere */ }
  }
  return bad;
}

Type guard

import { StrictModeError } from './diagram-prepass';
function isStrictModeError(e: unknown): e is StrictModeError {
  return e instanceof StrictModeError;
}

Prevention

When it happens

Trigger: inlineLocalImages() under --strict where the resolved image path is a directory (e.g. ![x](./assets) where assets is a folder), a named pipe, or a device file. The existence check passes (it exists) but stat.isFile() returns false.

Common situations: Markdown accidentally points at a directory (missing filename); a prank/malicious doc referencing /dev/zero or a fifo; a broken build that left a directory where a PNG should be; a path whose extension is .png but is actually a folder.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/f34816b5f5bde5f8. Report an issue: GitHub.