mastra-ai/mastra · error · Error
writeMjpegAviFile: at least one frame is required
Error message
writeMjpegAviFile: at least one frame is required
What it means
writeMjpegAviFile refuses to mux an AVI from an empty frame list. A zero-frame MJPEG AVI would contain an empty video stream, so the writer throws before creating any file instead of producing a corrupt/empty video.
Source
Thrown at packages/core/src/browser/recording/mjpeg-avi.ts:72
const MAX_U32 = 0xffffffff;
const MAX_I16 = 0x7fff;
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;View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure recording runs long enough for at least one frame to be captured before stopping
- Check state.frames.length > 0 before calling writeMjpegAviFile / encodeFramesAsAvi
- Verify screen-capture permissions so frames are actually captured
- If a zero-frame recording is legitimate, skip muxing at the caller and report an empty recording instead
Example fix
// before
writeMjpegAviFile(outPath, frames, opts);
// after
if (frames.length === 0) {
throw new Error('cannot write AVI: no frames were captured');
}
writeMjpegAviFile(outPath, frames, opts); Defensive patterns
Strategy: validation
Validate before calling
function canMux(frames) {
return Array.isArray(frames) && frames.length > 0;
}
if (!canMux(frames)) throw new Error('refusing to mux: no frames'); Type guard
function hasFrames(frames) {
return Array.isArray(frames) && frames.length > 0;
} Try / catch
try {
writeMjpegAviFile(outPath, frames, opts);
} catch (e) {
if (e.message.includes('at least one frame is required')) {
// treat as empty recording: skip or log
} else throw e;
} Prevention
- Check frames.length before stopping a recording
- Verify capture permissions before startRecording
- Keep recordings running longer than one capture interval
When it happens
Trigger: Calling writeMjpegAviFile(filePath, [], opts) directly, or indirectly via encodeFramesAsAvi when RecordingState.frames is empty — e.g. stopping browser_record before any screenshot interval captured a frame.
Common situations: Stopping a recording immediately after start (faster than the capture interval); screen capture permission denied so no frames were grabbed; browser tab closed before first frame; frames cleared by an earlier error path.
Related errors
- fourcc must be 4 ASCII characters, got "${s}"
- writeMjpegAviFile: invalid dimensions ${opts.width}x${opts.h
- No frames captured during recording
- Frame dimensions changed during recording: expected ${width}
- Unknown content type: ${(content as any).type}
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/24333645a0aaea27.
Report an issue: GitHub.