{"record":{"id":"0053637e77d3ab9e","repo":"affaan-m/ECC","slug":"ffmpeg-failed-proc-stderr-400-render3d","errorCode":null,"errorMessage":"ffmpeg failed: {proc.stderr[-400:]}","messagePattern":"ffmpeg failed: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/taste-distillation/scripts/taste/render3d.py","lineNumber":288,"sourceCode":"        except Exception:\n            # A failed Blender render must not lose the asset; the software\n            # path always works, so degrade instead of raising.\n            pass\n    return turntable_software(mesh_path, dest, n_frames, size), \"software\"\n\n\ndef frames_to_video(frames: list[Path], dst: str | Path, fps: float = 24.0) -> Path:\n    \"\"\"Encode rendered frames into a clip the rest of the pipeline can eat.\"\"\"\n    dst = Path(dst)\n    dst.parent.mkdir(parents=True, exist_ok=True)\n    pattern = str(frames[0].parent / \"turn_%04d.png\")\n    proc = subprocess.run([\n        \"ffmpeg\", \"-nostdin\", \"-loglevel\", \"error\", \"-y\",\n        \"-framerate\", f\"{fps:g}\", \"-i\", pattern,\n        \"-c:v\", \"libx264\", \"-crf\", \"14\", \"-pix_fmt\", \"yuv420p\", str(dst),\n    ], capture_output=True, text=True)\n    if proc.returncode != 0:\n        raise RuntimeError(f\"ffmpeg failed: {proc.stderr[-400:]}\")\n    return dst\n","sourceCodeStart":270,"sourceCodeEnd":290,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-distillation/scripts/taste/render3d.py#L270-L290","documentation":"frames_to_video encodes a numbered PNG sequence (e.g. turn_%04d.png) into an H.264 video with ffmpeg. If ffmpeg exits non-zero, it raises RuntimeError with the last 400 characters of ffmpeg's stderr. Since the input is a filename pattern, the usual failure is that the pattern matched nothing or the frames are malformed, which ffmpeg reports as an impossible-to-open input.","triggerScenarios":"frames_to_video(pattern, dst) fails when: the pattern doesn't match actual filenames (wrong padding, e.g. %03d vs turn_0001.png), frames live in a different directory than the pattern assumes, ffmpeg lacks libx264, dst is unwritable, or a source frame is corrupt so decoding aborts.","commonSituations":"Off-by-one start number (sequence starts at 0 or 1 while the pattern implies another), renamed/alphabetically-sorted frames with inconsistent zero padding, headless CI ffmpeg without libx264, spaces or special chars in the unquoted pattern, or dst in a read-only output directory.","solutions":["Read the stderr tail in the exception - 'No such file or directory' means the pattern matched nothing; 'Unknown encoder libx264' means the codec is missing.","Verify the pattern against the real files: ls the frames directory and confirm padding/start number; add -start_number to the ffmpeg input options if the sequence starts at 1 (or adjust your pattern).","Regenerate frames if corrupt, and make sure the glob used for counting matches the ffmpeg pattern exactly.","Confirm libx264 is available (ffmpeg -version shows --enable-libx264) or switch to an available codec (e.g. -c:v mpeg4).","Check dst's directory is writable and has space; quote any pattern containing spaces when constructing the command."],"exampleFix":"// before\nframes_to_video('renders/turn_%03d.png', 'out.mp4')  # files are turn_0001.png (4-digit, starts at 1)\n// after\nsubprocess.run(['ffmpeg','-y','-framerate','24','-start_number','1','-i','renders/turn_%04d.png','-c:v','libx264','-pix_fmt','yuv420p','out.mp4'], check=True)","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport re\n# confirm the pattern matches the frames that actually exist\nframes = sorted(Path(pattern_dir).glob('turn_*.png'))\nassert frames, f'no frames match in {pattern_dir}'\nnums = [int(re.search(r'(\\d+)\\.png$', f.name).group(1)) for f in frames]\npadding = len(re.search(r'\\d+', frames[0].name).group(0))\nprint(f'{len(frames)} frames, start={min(nums)}, padding={padding}')  # build pattern from these facts","typeGuard":null,"tryCatchPattern":"try:\n    frames_to_video(pattern, dst)\nexcept RuntimeError as e:\n    msg = str(e).split(': ', 1)[-1]\n    if 'No such file' in msg:\n        raise SystemExit('frame pattern mismatch: check padding and -start_number') from e\n    if 'libx264' in msg:\n        raise SystemExit('ffmpeg lacks libx264; install a full build') from e\n    raise","preventionTips":["Derive the ffmpeg pattern from the actual frame filenames (padding + start number), don't assume %04d from 1.","Write frames with consistent zero padding from the rendering step.","Verify libx264 in ffmpeg -version for each deployment environment.","Ensure the output directory exists and is writable before encoding; encode to a temp file then rename."],"tags":["ffmpeg","subprocess","video","encoding"],"backgroundTag":"ffmpeg-command-failed","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}