mastra-ai/mastra · error · Error

writeMjpegAviFile: dimensions exceed AVI header bounds: ${op

Error message

writeMjpegAviFile: dimensions exceed AVI header bounds: ${opts.width}x${opts.height}

What it means

AVI stream headers store width and height as 16-bit-ish bounded integer fields (MAX_I16 here), so writeMjpegAviFile rejects dimensions that cannot be represented in the AVI header, preventing a corrupt file.

Source

Thrown at packages/core/src/browser/recording/mjpeg-avi.ts:78

  }
  return Buffer.from(s, 'ascii');
}

/**
 * Encode a list of MJPEG frames as an AVI 1.0 file.
 *
 * The file is written incrementally to disk so we don't have to hold the whole
 * AVI in memory — large recordings can be hundreds of MB.
 */
export function writeMjpegAviFile(filePath: string, frames: readonly MjpegFrame[], opts: MjpegAviOptions): void {
  if (frames.length === 0) {
    throw new Error('writeMjpegAviFile: at least one frame is required');
  }
  if (opts.width <= 0 || opts.height <= 0 || !Number.isInteger(opts.width) || !Number.isInteger(opts.height)) {
    throw new Error(`writeMjpegAviFile: invalid dimensions ${opts.width}x${opts.height}`);
  }
  if (opts.width > MAX_I16 || opts.height > MAX_I16) {
    throw new Error(`writeMjpegAviFile: dimensions exceed AVI header bounds: ${opts.width}x${opts.height}`);
  }
  for (let i = 0; i < frames.length; i++) {
    assertJpegFrame(frames[i]!.bytes, i);
  }

  mkdirSync(dirname(filePath), { recursive: true });

  // Frame rate: derived from the elapsed time between the first and last
  // captured frames. AVI's main header stores a single dwMicroSecPerFrame, so
  // playback is even-paced; that matches MJPEG's typical usage.
  const elapsedMs =
    frames.length > 1 ? Math.max(1, frames[frames.length - 1]!.timestampMs - frames[0]!.timestampMs) : 1000;
  const fps = Math.max(1, Math.min(120, Math.round((frames.length * 1000) / elapsedMs)));
  const microSecPerFrame = Math.round(1_000_000 / fps);

  const maxFrameLen = frames.reduce((m, f) => Math.max(m, f.bytes.length), 0);
  assertU32(maxFrameLen, 'max frame length');
  const totalFrameBytes = frames.reduce((sum, f) => sum + CHUNK_HEADER_SIZE + paddedLength(f.bytes.length), 0);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Cap or scale the recording viewport to at most 32767x32767 before recording
  2. Avoid full-page screenshot capture for very long pages; use viewport-sized frames
  3. Check width/height <= 32767 before calling writeMjpegAviFile

Example fix

// before
writeMjpegAviFile(outPath, frames, { width: 40000, height: 1080, fps });
// after
const scale = Math.min(1, 32767 / width, 32767 / height);
writeMjpegAviFile(outPath, frames, {
  width: Math.round(width * scale),
  height: Math.round(height * scale),
  fps,
});
Defensive patterns

Strategy: validation

Validate before calling

const MAX_I16 = 32767;
if (width > MAX_I16 || height > MAX_I16) {
  const s = Math.min(MAX_I16 / width, MAX_I16 / height);
  width = Math.floor(width * s);
  height = Math.floor(height * s);
}

Type guard

function withinAviBounds(w, h) {
  return Number.isInteger(w) && w > 0 && w <= 32767 && Number.isInteger(h) && h > 0 && h <= 32767;
}

Try / catch

try {
  writeMjpegAviFile(outPath, frames, opts);
} catch (e) {
  if (e.message.includes('exceed AVI header bounds')) {
    // re-scale dimensions and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Passing opts.width or opts.height greater than MAX_I16 (32767), e.g. recording at 4K+ resolution or scaled dimensions beyond 32767px.

Common situations: Recording extremely tall/long pages via full-page screenshots (height can exceed 32767 on very long pages); stitching multi-monitor captures; wrong unit (twips/pixels) conversion inflating dimensions.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/abbeec49e1debe50. Report an issue: GitHub.