affaan-m/ECC · error · FileNotFoundError

LUT not found

Error message

LUT not found: {lut}

What it means

grade_clip applies a 3D LUT to a video clip via ffmpeg's lut3d filter. Before doing anything it resolves the LUT path to a Path and checks lut.exists(); if the file is not on disk it raises FileNotFoundError so the caller fails fast instead of letting ffmpeg die with a cryptic filter error. It is a guard against an invalid user-supplied path, not an internal fault.

Solutions

  1. Print and inspect the exact path: resolve it (Path(lut).resolve()) and check it exists with ls before re-running.
  2. Correct the path - fix typos and match case exactly; on Linux/macOS the filename is case-sensitive.
  3. If the pack is missing or incomplete, regenerate it by running mint.py so the .cube LUT is written to disk.
  4. Use an absolute path (or resolve relative paths against the pack root) instead of a cwd-dependent relative path.
  5. Wrap the call in try/except FileNotFoundError to surface a friendly message listing valid LUTs from the pack manifest.

Example fix

// before
grade_clip('shot.mp4', 'graded.mp4', lut='looks/teal_orange.CUBE')
// after
from pathlib import Path
lut = Path('looks/teal_orange.cube')
if not lut.exists():
    raise SystemExit(f'LUT missing: {lut.resolve()}')
grade_clip('shot.mp4', 'graded.mp4', lut=lut)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
lut = Path(lut_arg)
if not lut.is_file():
    raise SystemExit(f'LUT does not exist: {lut.resolve()}')
if lut.suffix.lower() != '.cube':
    raise SystemExit(f'expected a .cube LUT, got: {lut.name}')

Try / catch

try:
    grade_clip(src, dst, lut=lut)
except FileNotFoundError as e:
    logger.error('LUT missing, check pack manifest: %s', e)
    available = [p.name for p in pack_dir.glob('*.cube')]
    raise SystemExit(f'available LUTs: {available}') from e

Prevention

When it happens

Trigger: Calling grade_clip(src, dst, lut=...) with a lut argument that points to a missing file: a typo in the filename, a .cube file that was never minted/copied into the pack, a relative path resolved from the wrong working directory, or a pack directory that was moved or deleted after minting.

Common situations: Hand-writing LUT paths in a pipeline config instead of taking them from the StylePack manifest; running the script from a different cwd than during testing so relative paths break; case-sensitive filesystems where 'Look.CUBE' vs 'look.cube' matters on Linux; partial style packs where LUT generation was skipped.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-distillation/scripts/taste/grade.py:663

    lut: str | Path,
    strength: float = 1.0,
    crf: int = 16,
) -> Path:
    """Apply a pack's .cube to a clip with ffmpeg. This is where the look happens.

    Measured on three generations against the flashethereal pack: prompting
    for the grade moved midtone a* from +1.9 to +2.8 across two paid attempts
    and never touched contrast (23.4 / 19.3 / 19.2 against a target of 34.7).
    Running the same footage through this function put chroma within a mean
    absolute error of 1.4 and contrast at 34.9 against 34.7 - in one pass, at
    no marginal cost, and identically every time.

    ``strength`` below 1.0 blends the graded result back toward the original,
    for when the full pack look is too much for a particular shot.
    """
    src, dst, lut = Path(src), Path(dst), Path(lut)
    if not lut.exists():
        raise FileNotFoundError(f"LUT not found: {lut}")
    dst.parent.mkdir(parents=True, exist_ok=True)

    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)

View on GitHub (pinned to 8321021c54)