Hmbown/CodeWhale · error · ExecError

unrecognized raster format

Error message

unrecognized raster format: ${file}

What it means

`imagePixels` in the darwin backend sniffs the first bytes of a screenshot file to read image dimensions. It recognizes PNG (0x89 'PNG') and JPEG (0xFF 0xD8) magic numbers; anything else raises `unrecognized raster format: <file>`. It only reads the header, so it supports exactly these two formats.

Solutions

  1. Ensure the capture pipeline writes PNG (default) or JPEG, e.g. `screencapture -t png <file>`.
  2. Verify the file exists and is non-empty before calling imagePixels.
  3. Convert other formats to PNG/JPEG before dimension lookup.
  4. Check the path — the file may be an HTML error page or placeholder, not the screenshot.

Example fix

// before\nexec("screencapture -t tiff /tmp/shot.tif");\nimagePixels("/tmp/shot.tif"); // throws\n// after\nexec("screencapture -t png /tmp/shot.png");\nimagePixels("/tmp/shot.png");
Defensive patterns

Strategy: fallback

Validate before calling

const fd = fs.openSync(file, "r");\nconst head = Buffer.alloc(2); fs.readSync(fd, head, 0, 2, 0); fs.closeSync(fd);\nconst isPng = head[0] === 0x89, isJpeg = head[0] === 0xff && head[1] === 0xd8;\nif (!isPng && !isJpeg) throw new Error(`${file} is not PNG/JPEG`);

Type guard

const isSupportedRaster = (buf) => buf[0] === 0x89 || (buf[0] === 0xff && buf[1] === 0xd8);

Try / catch

try { return imagePixels(file); } catch (e) { if (/unrecognized raster format/.test(e.message)) return dimsViaSips(file); throw e; }

Prevention

When it happens

Trigger: Calling imagePixels (screenshot dimension lookup) on a file that is not PNG or JPEG — e.g. a TIFF screenshot from an older macOS capture path, a WebP/HEIC image, an empty file, or a text/HTML placeholder written where an image was expected.

Common situations: macOS screencapture configured for a different format (screencapture -t tiff), a truncated or zero-byte file whose header is unreadable, or a path pointing at the wrong file.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/c7a36a6d87520976. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:42

 * CODEWHALE_CU_MAX_IMAGE_BYTES in mcp/server.mjs, which keeps the hard guard.
 */
const rasterByteBudget = () => (Number(process.env.CODEWHALE_CU_MAX_IMAGE_BYTES) > 0
  ? Number(process.env.CODEWHALE_CU_MAX_IMAGE_BYTES)
  : 5_000_000);

/**
 * Pixel dimensions from a PNG IHDR or a JPEG frame header, reading only the
 * bytes that carry them rather than the whole raster.
 */
function imagePixels(file) {
  const fd = fs.openSync(file, "r");
  try {
    const head = Buffer.alloc(24);
    fs.readSync(fd, head, 0, 24, 0);
    if (head[0] === 0x89 && head.toString("ascii", 1, 4) === "PNG") {
      return { w: head.readUInt32BE(16), h: head.readUInt32BE(20) };
    }
    if (head[0] !== 0xff || head[1] !== 0xd8) throw new ExecError(`unrecognized raster format: ${file}`);
    // Walk JPEG segments to the frame header. SOF0/1/2/3/5..7/9..11/13..15
    // carry the dimensions; DHT/DQT and the rest are skipped by their length.
    const size = fs.fstatSync(fd).size;
    const seg = Buffer.alloc(9);
    for (let at = 2; at + 9 <= size; ) {
      fs.readSync(fd, seg, 0, 9, at);
      if (seg[0] !== 0xff) throw new ExecError(`malformed JPEG at byte ${at}: ${file}`);
      const marker = seg[1];
      if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
        return { w: seg.readUInt16BE(7), h: seg.readUInt16BE(5) };
      }
      if (marker === 0xd8 || (marker >= 0xd0 && marker <= 0xd9)) { at += 2; continue; }
      at += 2 + seg.readUInt16BE(2);
    }
    throw new ExecError(`JPEG carries no frame header: ${file}`);
  } finally {
    fs.closeSync(fd);
  }

View on GitHub (pinned to 73e0f67d83)