affaan-m/ECC · error · RuntimeError

ffmpeg grade failed

Error message

ffmpeg grade failed: {proc.stderr[-400:]}

What it means

`grade_clip()` shells out to FFmpeg with `-loglevel error` and raises a RuntimeError when the ffmpeg process exits non-zero, including the last 400 bytes of FFmpeg's stderr. This surfaces real encoding/filter failures (bad input file, unsupported filter syntax, codec issues, disk full). The truncated stderr is the primary diagnostic for why the grade failed.

Solutions

  1. Read the embedded stderr (last 400 chars in the exception message) — it names the exact FFmpeg failure.
  2. Check the source video with `ffprobe`; re-encode or remux if it is corrupt.
  3. Upgrade FFmpeg (`ffmpeg -version`) to one supporting `lut3d`; quote/escape the LUT path if it contains spaces.
  4. Test writing to the destination directory (`touch dst_dir/.probe`) to rule out permission problems.

Example fix

// before
vf = f"lut3d=file='{lut.as_posix()}"   # broken quoting -> ffmpeg fails
// after
vf = f"lut3d=file='{lut.as_posix()}'"  # properly closed quote
subprocess.run(cmd, capture_output=True, text=True, check=False)
Defensive patterns

Strategy: try-catch

Validate before calling

import shutil
assert shutil.which('ffmpeg'), 'ffmpeg not on PATH'
assert Path(src).is_file(), f'missing source: {src}'

Try / catch

try:
    grade_clip(src, dst, lut)
except RuntimeError as e:
    if str(e).startswith('ffmpeg grade failed'):
        logger.error('ffmpeg stderr: %s', e)
        raise GradeError(dst, stderr=str(e)) from e
    raise

Prevention

When it happens

Trigger: Calling `grade_clip()` where the subprocess returns returncode != 0 — e.g. the source video is corrupt/unreadable, the lut3d filter path quoting fails, the output path is unwritable, or ffmpeg is missing/misbehaving.

Common situations: Spaces or special characters in LUT paths breaking the `file='...'` filter quoting; source video with unsupported pixel format for libx264; destination directory lacking write permission; old FFmpeg without lut3d support.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/79340a4ca20c4d21. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/taste/grade.py:683

    s = max(0.0, min(1.0, float(strength)))
    if s >= 0.999:
        vf = f"lut3d=file='{lut.as_posix()}'"
    else:
        # Blend graded over original so partial looks stay available.
        vf = (
            f"split=2[a][b];[b]lut3d=file='{lut.as_posix()}'[g];"
            f"[a][g]blend=all_mode=normal:all_opacity={s:.3f}"
        )

    cmd = [
        "ffmpeg", "-nostdin", "-loglevel", "error", "-y", "-i", str(src),
        "-vf", vf, "-c:v", "libx264", "-crf", str(crf), "-pix_fmt", "yuv420p",
        "-c:a", "copy", str(dst),
    ]
    proc = subprocess.run(cmd, capture_output=True, text=True)
    if proc.returncode != 0:
        raise RuntimeError(f"ffmpeg grade failed: {proc.stderr[-400:]}")
    return dst


def grade_clip_adaptive(
    src: str | Path,
    dst: str | Path,
    target: GradeStats,
    strength: float = 1.0,
    lut_size: int = 33,
    n_frames: int = 32,
    keep_lut: str | Path | None = None,
) -> Path:
    """Measure the clip, bake a LUT *for that clip*, then apply it.

    Prefer this over :func:`grade_clip` for anything generated.

    ``look.cube`` is baked against a canonical neutral stand-in, because when
    a pack is minted there is no way to know what footage it will meet. That

View on GitHub (pinned to 8321021c54)