{"record":{"id":"72cce235f7bd8700","repo":"invoke-ai/InvokeAI","slug":"unable-to-open-video-at-video-path-72cce2","errorCode":null,"errorMessage":"Unable to open video at {video_path}","messagePattern":"Unable to open video at (.+?)","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"invokeai/app/util/video_thumbnails.py","lineNumber":419,"sourceCode":"        Path(tmp_name).unlink(missing_ok=True)\n\n\ndef probe_video_with_codec(\n    video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS\n) -> tuple[int, int, float, Optional[float], Optional[str]]:\n    \"\"\"Returns (width, height, duration_seconds, fps_or_none, codec_or_none) for a video file.\n\n    Raises FileNotFoundError if the file cannot be read — including when the decode\n    times out, since a file we cannot probe within the bound is treated as unreadable —\n    or when the decoder reports metadata no sane video has (non-positive or over-limit\n    dimensions, non-finite or negative duration). Decoder-reported values are untrusted:\n    they come from the uploaded container, and the upload path persists them and sizes\n    thumbnail decoding by them. A non-finite or non-positive fps is coerced to None\n    (unknown) rather than rejected, matching the decoder's own unknown-fps behavior.\n    \"\"\"\n    result = _run_worker([\"probe\", str(video_path)], timeout)\n    if result is None:\n        raise FileNotFoundError(f\"Unable to open video at {video_path}\")\n    try:\n        width = int(result[\"width\"])\n        height = int(result[\"height\"])\n        duration = float(result[\"duration\"])\n        fps_raw = result.get(\"fps\")\n        fps: Optional[float] = float(fps_raw) if fps_raw else None\n        codec_raw = result.get(\"codec\")\n        codec = str(codec_raw).lower() if codec_raw else None\n    except (KeyError, TypeError, ValueError, OverflowError) as e:\n        raise FileNotFoundError(f\"Unable to open video at {video_path}\") from e\n    if width <= 0 or height <= 0 or width * height > MAX_VIDEO_FRAME_PIXELS:\n        raise FileNotFoundError(f\"Video at {video_path} reports invalid dimensions {width}x{height}\")\n    if not math.isfinite(duration) or duration < 0:\n        raise FileNotFoundError(f\"Video at {video_path} reports an invalid duration {duration}\")\n    if fps is not None and (not math.isfinite(fps) or fps <= 0):\n        fps = None\n    return width, height, duration, fps, codec\n","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/util/video_thumbnails.py#L401-L437","documentation":"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'.","triggerScenarios":"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).","commonSituations":"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'.","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."],"exampleFix":"// before\ninfo = probe_video(path)  # FileNotFoundError: Unable to open video at path\n// after\nif not path.is_file() or path.stat().st_size == 0:\n    raise MissingUploadError(path)\ninfo = probe_video(path, timeout=60)\nif info is None:\n    raise UnsupportedMediaError(path)","handlingStrategy":"validation","validationCode":"import os\ndef probe_target_ready(path: str) -> bool:\n    p = os.path.realpath(path)\n    return os.path.isfile(p) and os.access(p, os.R_OK) and os.path.getsize(p) > 0","typeGuard":null,"tryCatchPattern":"try:\n    info = probe_video(path, timeout=60)\nexcept FileNotFoundError as e:\n    if \"Unable to open video\" in str(e):\n        return None  # treat as unsupported/missing, surface 415 to user","preventionTips":["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"],"tags":["video","probe","file-not-found","validation"],"backgroundTag":"video-file-unopenable","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}