{"record":{"id":"24333645a0aaea27","repo":"mastra-ai/mastra","slug":"writemjpegavifile-at-least-one-frame-is-required","errorCode":null,"errorMessage":"writeMjpegAviFile: at least one frame is required","messagePattern":"writeMjpegAviFile: at least one frame is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/browser/recording/mjpeg-avi.ts","lineNumber":72,"sourceCode":"const MAX_U32 = 0xffffffff;\nconst MAX_I16 = 0x7fff;\n\nfunction fourcc(s: string): Buffer {\n  if (s.length !== 4) {\n    throw new Error(`fourcc must be 4 ASCII characters, got \"${s}\"`);\n  }\n  return Buffer.from(s, 'ascii');\n}\n\n/**\n * Encode a list of MJPEG frames as an AVI 1.0 file.\n *\n * The file is written incrementally to disk so we don't have to hold the whole\n * AVI in memory — large recordings can be hundreds of MB.\n */\nexport function writeMjpegAviFile(filePath: string, frames: readonly MjpegFrame[], opts: MjpegAviOptions): void {\n  if (frames.length === 0) {\n    throw new Error('writeMjpegAviFile: at least one frame is required');\n  }\n  if (opts.width <= 0 || opts.height <= 0 || !Number.isInteger(opts.width) || !Number.isInteger(opts.height)) {\n    throw new Error(`writeMjpegAviFile: invalid dimensions ${opts.width}x${opts.height}`);\n  }\n  if (opts.width > MAX_I16 || opts.height > MAX_I16) {\n    throw new Error(`writeMjpegAviFile: dimensions exceed AVI header bounds: ${opts.width}x${opts.height}`);\n  }\n  for (let i = 0; i < frames.length; i++) {\n    assertJpegFrame(frames[i]!.bytes, i);\n  }\n\n  mkdirSync(dirname(filePath), { recursive: true });\n\n  // Frame rate: derived from the elapsed time between the first and last\n  // captured frames. AVI's main header stores a single dwMicroSecPerFrame, so\n  // playback is even-paced; that matches MJPEG's typical usage.\n  const elapsedMs =\n    frames.length > 1 ? Math.max(1, frames[frames.length - 1]!.timestampMs - frames[0]!.timestampMs) : 1000;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/browser/recording/mjpeg-avi.ts#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nwriteMjpegAviFile(outPath, frames, opts);\n// after\nif (frames.length === 0) {\n  throw new Error('cannot write AVI: no frames were captured');\n}\nwriteMjpegAviFile(outPath, frames, opts);","handlingStrategy":"validation","validationCode":"function canMux(frames) {\n  return Array.isArray(frames) && frames.length > 0;\n}\nif (!canMux(frames)) throw new Error('refusing to mux: no frames');","typeGuard":"function hasFrames(frames) {\n  return Array.isArray(frames) && frames.length > 0;\n}","tryCatchPattern":"try {\n  writeMjpegAviFile(outPath, frames, opts);\n} catch (e) {\n  if (e.message.includes('at least one frame is required')) {\n    // treat as empty recording: skip or log\n  } else throw e;\n}","preventionTips":["Check frames.length before stopping a recording","Verify capture permissions before startRecording","Keep recordings running longer than one capture interval"],"tags":["avi","muxing","input-validation","recording"],"backgroundTag":"empty-frame-recording","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}