{"record":{"id":"0b8c4ab09d788738","repo":"heygen-com/hyperframes","slug":"ffmpeg-could-not-extract-first-frame-from-videop","errorCode":null,"errorMessage":"ffmpeg could not extract first frame from ${videoPath}${detail}","messagePattern":"ffmpeg could not extract first frame from (.+?)(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/commands/grade-compare.ts","lineNumber":509,"sourceCode":"      \"error\",\n      \"-ss\",\n      \"0\",\n      \"-i\",\n      videoPath,\n      \"-frames:v\",\n      \"1\",\n      \"-q:v\",\n      \"2\",\n      \"-y\",\n      outPath,\n    ];\n    const result = await runFfmpegOnce(ffmpegPath, args, FFMPEG_EXTRACT_TIMEOUT_MS);\n    if (result.timedOut) {\n      throw new Error(`ffmpeg timed out extracting first frame from ${videoPath}`);\n    }\n    if (result.code !== 0 || !existsSync(outPath)) {\n      const detail = result.stderr.trim() ? `: ${result.stderr.trim()}` : \"\";\n      throw new Error(`ffmpeg could not extract first frame from ${videoPath}${detail}`);\n    }\n    return readFileSync(outPath);\n  } finally {\n    rmSync(tmp, { recursive: true, force: true });\n  }\n}\n\nasync function loadReferenceFrame(framePath: string): Promise<ReferenceFrame> {\n  if (!existsSync(framePath)) {\n    throw new Error(`Reference frame not found: ${framePath}`);\n  }\n\n  const buffer = isVideoPath(framePath)\n    ? await extractVideoFrameToBuffer(framePath)\n    : readFileSync(framePath);\n  if (!buffer) {\n    throw new Error(`Could not extract a frame from video: ${framePath}`);\n  }","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/grade-compare.ts#L491-L527","documentation":"Thrown by extractVideoFrameToBuffer when ffmpeg exited with a non-zero code OR failed to write the expected output PNG within the timeout. The trimmed stderr is appended (`detail`) so the underlying ffmpeg complaint is surfaced rather than swallowed. This is the non-timeout failure branch, complementing the timeout branch at line 505.","triggerScenarios":"Passing `--for video.mp4` where ffmpeg runs to completion but errors: unsupported codec, unreadable container, missing decoder, permission denied on the temp dir, or an ffmpeg build without the needed codec flags.","commonSituations":"A minimal/static ffmpeg build lacking libx264 or HEVC support; a DRM-protected or truncated video; permission issues on the OS temp dir; an unsupported container like a raw stream with no demuxer.","solutions":["Read the appended ffmpeg stderr in the message — it names the actual decoder/format problem.","Install a full ffmpeg build (e.g. the static `ffmpeg` with all GPL codecs) if the message indicates a missing decoder.","Pre-extract the frame with your own ffmpeg invocation and pass the PNG via `--for`.","Confirm the temp directory is writable (`mktemp -d`) if the error hints at output write failure."],"exampleFix":"# before — ffmpeg lacks the decoder\nhyperframes grade-compare --for hevc_clip.mp4 --grades g.json\n# after — extract with a full ffmpeg, then compare the PNG\nffmpeg -i hevc_clip.mp4 -frames:v 1 frame.png\nhyperframes grade-compare --for frame.png --grades g.json","handlingStrategy":"fallback","validationCode":"// Probe the video with ffprobe before grading to catch codec issues\nimport { spawnSync } from \"node:child_process\";\nfunction videoIsReadable(path: string): boolean {\n  const r = spawnSync(\"ffprobe\", [\"-v\", \"error\", \"-select_streams\", \"v:0\", \"-show_entries\", \"stream=codec_name\", path]);\n  return r.status === 0 && /codec_name/.test(r.stdout.toString());\n}","typeGuard":null,"tryCatchPattern":"try {\n  // run grade-compare on a video\n} catch (err) {\n  if (/ffmpeg could not extract/.test((err as Error).message))) {\n    // read appended stderr, install missing codec, or pre-extract the frame\n  }\n}","preventionTips":["Install a full ffmpeg build with all GPL codecs for HEVC/AV1 input.","Run `ffmpeg -version` to confirm codec support flags before grading videos.","Pre-extract frames in your pipeline so the CLI never needs to decode video."],"tags":["cli","ffmpeg","video","grade-compare","codec"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}