invoke-ai/InvokeAI · error · FileNotFoundError
Unable to open video at {video_path}
Error message
Unable to open video at {video_path} What it means
Raised (as FileNotFoundError) by probe_video_with_codec when the decode worker's 'probe' command returns None, meaning the worker could not open the video (cv2/ffmpeg open failed) or the worker run failed. It reports the video as unopenable rather than returning partial metadata. Callers (_probe_decodable_video, probe_video) treat this as 'not a decodable video'.
Source
Thrown at invokeai/app/util/video_thumbnails.py:419
Path(tmp_name).unlink(missing_ok=True)
def probe_video_with_codec(
video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS
) -> tuple[int, int, float, Optional[float], Optional[str]]:
"""Returns (width, height, duration_seconds, fps_or_none, codec_or_none) for a video file.
Raises FileNotFoundError if the file cannot be read — including when the decode
times out, since a file we cannot probe within the bound is treated as unreadable —
or when the decoder reports metadata no sane video has (non-positive or over-limit
dimensions, non-finite or negative duration). Decoder-reported values are untrusted:
they come from the uploaded container, and the upload path persists them and sizes
thumbnail decoding by them. A non-finite or non-positive fps is coerced to None
(unknown) rather than rejected, matching the decoder's own unknown-fps behavior.
"""
result = _run_worker(["probe", str(video_path)], timeout)
if result is None:
raise FileNotFoundError(f"Unable to open video at {video_path}")
try:
width = int(result["width"])
height = int(result["height"])
duration = float(result["duration"])
fps_raw = result.get("fps")
fps: Optional[float] = float(fps_raw) if fps_raw else None
codec_raw = result.get("codec")
codec = str(codec_raw).lower() if codec_raw else None
except (KeyError, TypeError, ValueError, OverflowError) as e:
raise FileNotFoundError(f"Unable to open video at {video_path}") from e
if width <= 0 or height <= 0 or width * height > MAX_VIDEO_FRAME_PIXELS:
raise FileNotFoundError(f"Video at {video_path} reports invalid dimensions {width}x{height}")
if not math.isfinite(duration) or duration < 0:
raise FileNotFoundError(f"Video at {video_path} reports an invalid duration {duration}")
if fps is not None and (not math.isfinite(fps) or fps <= 0):
fps = None
return width, height, duration, fps, codec
View on GitHub (pinned to 0b6a024f2f)
Solutions
- Check os.path.isfile(path) and read permissions before probing.
- Distinguish 'bad file' from 'busy decoder': a None/timeout under load is not corrupt — retry with a longer timeout or check worker logs.
- Probe only after the upload is complete and the file is flushed/closed.
- Validate the container with ffprobe -v error -select_streams v:0 to confirm a real video stream before calling the library.
- Catch FileNotFoundError and return a user-facing 'unsupported or missing video' error.
Example fix
// before
info = probe_video(path) # FileNotFoundError: Unable to open video at path
// after
if not path.is_file() or path.stat().st_size == 0:
raise MissingUploadError(path)
info = probe_video(path, timeout=60)
if info is None:
raise UnsupportedMediaError(path) Defensive patterns
Strategy: validation
Validate before calling
import os
def probe_target_ready(path: str) -> bool:
p = os.path.realpath(path)
return os.path.isfile(p) and os.access(p, os.R_OK) and os.path.getsize(p) > 0 Try / catch
try:
info = probe_video(path, timeout=60)
except FileNotFoundError as e:
if "Unable to open video" in str(e):
return None # treat as unsupported/missing, surface 415 to user Prevention
- Probe only after upload is fully written and closed
- Check existence/permissions before probing
- Retry with a longer timeout if slots may be busy (None ≠ corrupt)
- Confirm the container has a video stream with ffprobe first
When it happens
Trigger: Calling probe_video/probe_video_with_codec on a non-existent path, a path with permission denied, a file whose container OpenCV/FFmpeg cannot open, an empty file, or when _run_worker returned None due to slot exhaustion or timeout (raise_on_timeout=False).
Common situations: Uploads not yet fully written to disk when probing; files deleted between upload and probe; formats like MKV with unusual headers, or MOV files from drones with exotic codecs; transient slot exhaustion under load being misread as 'bad file'.
Related errors
- video_concat requires at least two input videos.
- All inputs must share the same dimensions. Got: {sorted(widt
- The requested transition needs an estimated {estimated_mib:.
- Clip {i} has {n_frames} frames but the requested transitions
- Cannot resolve negative frame index for video {self.video.vi
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/72cce235f7bd8700.
Report an issue: GitHub.