mastra-ai/mastra · error · Error

writeMjpegAviFile: invalid dimensions ${opts.width}x${opts.h

Error message

writeMjpegAviFile: invalid dimensions ${opts.width}x${opts.height}

What it means

writeMjpegAviFile validates that opts.width and opts.height are positive integers before writing the AVI stream header, which stores dimensions in fixed-size binary fields that cannot represent zero, negative, or fractional values.

Source

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

function fourcc(s: string): Buffer {
  if (s.length !== 4) {
    throw new Error(`fourcc must be 4 ASCII characters, got "${s}"`);
  }
  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);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Round/sanitize dimensions with Math.round/Math.max(1, ...) before constructing MjpegAviOptions
  2. Ensure dimensions come from a successfully decoded frame (decodeJpeg(frame.bytes).width/height)
  3. Add a pre-call check: Number.isInteger(w) && w > 0 && Number.isInteger(h) && h > 0

Example fix

// before
writeMjpegAviFile(outPath, frames, { width: vw / 2, height: vh / 2, fps });
// after
writeMjpegAviFile(outPath, frames, {
  width: Math.max(1, Math.round(vw / 2)),
  height: Math.max(1, Math.round(vh / 2)),
  fps,
});
Defensive patterns

Strategy: validation

Validate before calling

function validDims(w, h) {
  return Number.isInteger(w) && w > 0 && Number.isInteger(h) && h > 0;
}
if (!validDims(opts.width, opts.height)) {
  throw new Error('width/height must be positive integers');
}

Type guard

function hasValidDimensions(o) {
  return typeof o?.width === 'number' && Number.isInteger(o.width) && o.width > 0
    && typeof o?.height === 'number' && Number.isInteger(o.height) && o.height > 0;
}

Try / catch

try {
  writeMjpegAviFile(outPath, frames, opts);
} catch (e) {
  if (e.message.includes('invalid dimensions')) {
    // fix dims from a decoded frame and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Passing MjpegAviOptions with width or height that is 0, negative, NaN, or fractional (e.g. 0x0, -1x480, 1920.5x1080).

Common situations: Dimensions derived from an uninitialized variable or failed image decode returning 0; computing dimensions by division without rounding; passing viewport size before the page finished loading.

Related errors


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