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

  1. Read the embedded ffmpeg stderr tail in the message — it states the exact filter/option failure; correct that argument.
  2. Run the printed command prefix manually with full args and -loglevel verbose to reproduce and see complete stderr.
  3. For overlay errors, validate element geometry fits within width/height before building the filtergraph (see the size-clamping already done in overlay()).
  4. Confirm all input files exist, are readable, and share codec/size where the operation assumes it (concat, normalize).
  5. 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

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)