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
- Read the embedded stderr (last 400 chars in the exception message) — it names the exact FFmpeg failure.
- Check the source video with `ffprobe`; re-encode or remux if it is corrupt.
- Upgrade FFmpeg (`ffmpeg -version`) to one supporting `lut3d`; quote/escape the LUT path if it contains spaces.
- 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
- Log the full ffmpeg stderr, not just the message
- Pin a modern FFmpeg version with lut3d and libx264 support
- Quote/escape paths inside -vf filter strings
- Free disk space and verify write permissions before encodes
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. ThatView on GitHub (pinned to 8321021c54)