{"record":{"id":"db76049ac4d02326","repo":"invoke-ai/InvokeAI","slug":"video-at-video-path-reports-an-invalid-duration","errorCode":null,"errorMessage":"Video at {video_path} reports an invalid duration {duration}","messagePattern":"Video at (.+?) reports an invalid duration (.+?)","errorType":"validation","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"invokeai/app/util/video_thumbnails.py","lineNumber":433,"sourceCode":"    (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\n\ndef probe_video(\n    video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS\n) -> tuple[int, int, float, Optional[float]]:\n    \"\"\"Returns validated video metadata without the codec.\"\"\"\n    width, height, duration, fps, _codec = probe_video_with_codec(video_path, timeout)\n    return width, height, duration, fps\n\n\ndef decoder_frame_count(video_path: Path, timeout: float = VIDEO_DECODE_TIMEOUT_SECONDS) -> Optional[int]:\n    \"\"\"Returns the exact decoded frame count, or None if it cannot be determined in time.\n\n    Preferred over a ``duration * fps`` estimate, which can overshoot by one on VFR\n    uploads or containers with imprecise metadata; callers fall back to that estimate","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/util/video_thumbnails.py#L415-L451","documentation":"probe_video_with_codec validates that the probed duration is a finite, non-negative float. If ffprobe reports NaN, inf, or a negative duration, the function raises FileNotFoundError because the metadata cannot be trusted for thumbnail timing.","triggerScenarios":"Calling probe_video on a video whose ffprobe JSON contains duration = 'N/A', NaN, INF, or a negative value — typically live streams, unfinalized recordings, or files with corrupted duration metadata.","commonSituations":"Live/HLS recordings without a fixed duration, partially downloaded files, webm/matroska containers with missing duration, or corrupt metadata after a crashed encoder.","solutions":["Run ffprobe -show_format <path> to inspect the reported duration.","Remux with ffmpeg (-c copy) or re-encode to regenerate valid duration metadata.","For live/unknown-duration sources, cap the recording to a fixed length before processing.","If needed, estimate duration from packet timestamps instead of the format tag."],"exampleFix":"# before\nduration = get_duration(path)  # uses probe_video\n# after\ntry:\n    duration = get_duration(path)\nexcept FileNotFoundError:\n    duration = None  # handle unknown-duration video explicitly\nif duration is None:\n    raise ValueError(f\"{path}: unknown duration; cannot schedule thumbnails\")","handlingStrategy":"validation","validationCode":"import subprocess, json\nout = subprocess.run([\"ffprobe\",\"-v\",\"error\",\"-print_format\",\"json\",\"-show_format\",path], capture_output=True)\ndur_raw = json.loads(out.stdout or \"{}\").get(\"format\", {}).get(\"duration\")\ntry:\n    dur = float(dur_raw)\nexcept (TypeError, ValueError):\n    raise ValueError(f\"{path}: duration missing or non-numeric ({dur_raw!r})\")\nimport math\nif not math.isfinite(dur) or dur < 0:\n    raise ValueError(f\"{path}: invalid duration {dur}\")","typeGuard":"import math\ndef has_valid_duration(probe: dict) -> bool:\n    try:\n        d = float(probe[\"format\"][\"duration\"])\n    except (KeyError, TypeError, ValueError):\n        return False\n    return math.isfinite(d) and d >= 0","tryCatchPattern":"try:\n    w, h, dur, fps, codec = probe_video(path)\nexcept FileNotFoundError as e:\n    dur = fallback_duration_from_packets(path)  # or reject\n    if dur is None:\n        raise ValueError(\"video has unknown duration\") from e","preventionTips":["Reject live streams / unknown-duration sources at ingestion.","Remux files lacking duration metadata before queuing.","Cap recordings to fixed durations.","Surface duration issues to users as 'unsupported video' rather than crashing."],"tags":["video","ffprobe","validation","metadata"],"backgroundTag":"invalid-video-metadata","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}