affaan-m/ECC · error · RuntimeError
ffmpeg failed: ...
Error message
ffmpeg failed: {' '.join(cmd[:6])}...
{proc.stderr[-400:]} What it means
assemble._run() invokes an ffmpeg subprocess with captured output; if ffmpeg exits non-zero it raises RuntimeError including the first six command words and the last 400 chars of stderr. This is the central ffmpeg failure reporter for all assemble operations (cut_take, overlay, concat, normalize).
Solutions
- Read the embedded ffmpeg stderr tail in the message — it states the exact filter/option failure; correct that argument.
- Run the printed command prefix manually with full args and -loglevel verbose to reproduce and see complete stderr.
- For overlay errors, validate element geometry fits within width/height before building the filtergraph (see the size-clamping already done in overlay()).
- Confirm all input files exist, are readable, and share codec/size where the operation assumes it (concat, normalize).
- Update ffmpeg to a recent build if the error mentions an unknown filter or option.
Example fix
// before
_run(["ffmpeg", "-i", src, "-vf", f"overlay={x}:{y}", out]) # x,y may exceed frame
// after
x = max(0, min(x, width - ew))
y = max(0, min(y, height - eh))
_run(["ffmpeg", "-nostdin", "-loglevel", "error", "-y", "-i", src,
"-vf", f"overlay={x}:{y}", out]) Defensive patterns
Strategy: try-catch
Validate before calling
import shutil
if shutil.which("ffmpeg") is None:
raise SystemExit("ffmpeg not found on PATH")
for src in inputs:
if not Path(src).exists():
raise SystemExit(f"input missing for ffmpeg: {src}") Type guard
null
Try / catch
try:
assemble.overlay(video, logo, ...)
except RuntimeError as exc:
log.error("ffmpeg step failed: %s", exc) # stderr tail is embedded
raise Prevention
- Always read the stderr tail embedded in the message before changing code
- Clamp overlay geometry to frame bounds before building filtergraphs
- Reproduce failures by running the command manually with -loglevel verbose
- Validate input codecs/sizes match where operations assume compatibility
- Keep ffmpeg updated to a recent build
When it happens
Trigger: Any _run-wrapped ffmpeg invocation returns returncode != 0: invalid filtergraph arguments (e.g. overlay pad geometry out of bounds), missing input file, unsupported codec, bad fps/size flags, or ffmpeg binary missing (though that usually raises FileNotFoundError instead).
Common situations: Overlay element sized/positioned outside the frame so pad() errors; concat list references missing clips; input videos with mismatched codecs/resolutions; ffmpeg too old for a filter flag used; disk full during encode.
Related errors
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/ff6255c109e77e34.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-distillation/scripts/taste/assemble.py:31
new material into it;
* **the cut itself**, at the reference's measured cadence rather than at
whatever length the generator happened to emit.
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from . import cadence as cad_mod
from . import frames as frame_mod
def _run(cmd: list[str]) -> None:
proc = subprocess.run(cmd, capture_output=True, text=True)
if proc.returncode != 0:
raise RuntimeError(f"ffmpeg failed: {' '.join(cmd[:6])}...\n{proc.stderr[-400:]}")
def cut_take(
src: str | Path,
shots: list[dict],
dest_dir: str | Path,
prefix: str = "shot",
fps: float | None = None,
) -> list[Path]:
"""Slice one generated take into its planned sub-shots.
Re-encodes rather than stream-copying. Stream copy can only cut on
keyframes, and at a mean shot length of 0.78s that rounds every boundary
to the nearest GOP - which is precisely the rhythm this whole pipeline
exists to preserve.
"""
src, dest_dir = Path(src), Path(dest_dir)
dest_dir.mkdir(parents=True, exist_ok=True)View on GitHub (pinned to 8321021c54)