affaan-m/ECC · error · RuntimeError
ffmpeg failed: ...
Error message
ffmpeg failed: {' '.join(cmd[:6])}...
{proc.stderr[-400:]} What it means
This RuntimeError is raised by the internal _run() helper in assemble.py whenever an ffmpeg subprocess exits with a non-zero return code. The message includes the first six argv tokens (to identify the operation: cut_take, overlay, concat, or normalize) and the last 400 characters of ffmpeg's stderr for diagnosis. It is a generic wrapper: the real cause is always in the captured stderr.
Solutions
- Read the stderr tail in the message (after the '...') — it contains ffmpeg's actual error; fix the option/filter it complains about
- Run the exact ffmpeg command shown in the message manually with full arguments to reproduce and iterate on the failure
- Verify the input file exists and is decodable: ffprobe <input>
- Check the output path/extension maps to a supported muxer/codec
- Confirm ffmpeg is installed and on PATH (ffmpeg -version) and matches expected version behavior
Example fix
# before
_run(["ffmpeg", "-i", src, "-vf", "pad=-10:-10", out]) # negative pad kills filtergraph
# after
_run(["ffmpeg", "-i", src, "-vf", f"pad={width}:{height}:0:0", out]) # valid non-negative offsets Defensive patterns
Strategy: try-catch
Validate before calling
import shutil
assert shutil.which("ffmpeg"), "ffmpeg not on PATH"
import subprocess
probe = subprocess.run(["ffprobe", "-v", "error", str(src)], capture_output=True, text=True)
assert probe.returncode == 0, f"undecodable input: {probe.stderr}" Try / catch
try:
cut_take(src, shots, dest)
except RuntimeError as e:
msg = str(e)
stderr_tail = msg.split("...", 1)[-1]
print(f"ffmpeg failed:\n{stderr_tail}") # inspect and fix the offending command
raise Prevention
- ffprobe every input before assembling
- Keep filtergraph strings simple and test pad/scale expressions with a single frame first
- Pin and verify the ffmpeg version in your environment
- Log the full command (not just the first 6 tokens) for debugging
When it happens
Trigger: Any call to cut_take(), overlay(), concat(), or normalize() where the constructed ffmpeg command fails — e.g. invalid filter graph syntax, missing input file, unsupported codec/parameters, or a malformed output path — since _run() propagates every subprocess failure as this RuntimeError.
Common situations: Building a filter_complex string with an invalid pad/scale expression; pointing cut_take at a source ffmpeg cannot decode; requesting an output format/extension ffmpeg doesn't recognize; a broken ffmpeg installation or wrong binary on PATH producing option errors.
Related errors
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/a085c1388fcb6f03.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/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)