affaan-m/ECC · error · MediaToolUnavailable
ffprobe is required for reference probing
Error message
ffprobe is required for reference probing
What it means
probe_media needs ffprobe on PATH to read timestamped style/temporal features from local media; shutil.which('ffprobe') returned nothing, so the environment is missing the ffmpeg toolset.
Solutions
- Install the full ffmpeg package so ffprobe ships alongside it (apt install ffmpeg)
- Verify with `which ffprobe` in the same environment that runs the workflow
- Fix PATH for the execution context (cron/systemd/CI often have a minimal PATH)
Example fix
# before $ which ffprobe # (empty) # after $ sudo apt-get install -y ffmpeg && which ffprobe /usr/bin/ffprobe
Defensive patterns
Strategy: validation
Validate before calling
import shutil
missing = [t for t in ("ffmpeg", "ffprobe") if shutil.which(t) is None]
if missing:
raise SystemExit(f"missing media tools: {', '.join(missing)}") Type guard
def has_ffprobe() -> bool:
import shutil
return shutil.which("ffprobe") is not None Try / catch
try:
probe = probe_media(path)
except MediaToolUnavailable as e:
logging.error("media tooling unavailable: %s", e)
raise SystemExit(2) Prevention
- Install full ffmpeg packages, not stripped builds
- Verify both ffmpeg and ffprobe resolve on PATH in deployment scripts
- Pin the ffmpeg package in Dockerfiles and CI
When it happens
Trigger: probe_media(path) invoked while shutil.which('ffprobe') is None — ffprobe not installed, or ffmpeg installed without ffprobe (rare minimal builds).
Common situations: Slim container images with ffmpeg but stripped ffprobe; partially installed/removed ffmpeg packages; PATH not including /usr/bin in service contexts.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- ENOENT
- ffmpeg is required for temporal/style feature extraction
- asset must contain a video stream
- ${capability.reason}: ${capability.action}
- discussion is not an Announcement
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/648cbfa834f3dee9.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/workflow.py:371
"scale=320:-2,"
"select='not(mod(n\\,12))+gt(scene\\,0.30)',"
"signalstats,metadata=print:file=-"
)
result = subprocess.run(
[ffmpeg, "-v", "error", "-i", str(path), "-vf", filters,
"-an", "-vsync", "0", "-f", "null", "-"],
check=True,
capture_output=True,
text=True,
)
return parse_feature_output(result.stderr + "\n" + result.stdout)
def probe_media(path: Path) -> dict[str, Any]:
"""Probe local media and extract timestamped style/temporal features."""
ffprobe = shutil.which("ffprobe")
if not ffprobe:
raise MediaToolUnavailable("ffprobe is required for reference probing")
command = [
ffprobe, "-v", "error", "-show_streams", "-show_format",
"-of", "json", str(path),
]
result = subprocess.run(command, check=True, capture_output=True, text=True)
payload = json.loads(result.stdout)
video: dict[str, Any] = next(
(stream for stream in payload.get("streams", []) if stream.get("codec_type") == "video"),
{},
)
duration = float(video.get("duration") or payload.get("format", {}).get("duration") or 0)
rate = str(video.get("avg_frame_rate") or video.get("r_frame_rate") or "0/1")
num, _, den = rate.partition("/")
fps = float(num) / float(den or 1) if float(den or 1) else 0.0
feature_data = _run_ffmpeg_features(path)
measured_times = sorted({
float(sample["time"]) for sample in feature_data["style_samples"]
} | set(feature_data["scene_changes"]))View on GitHub (pinned to 8321021c54)