roboflow/supervision · error · ValueError

Video source has no video stream: {source}

Error message

Video source has no video stream: {source}

What it means

Raised when the PyAV capture opens a file successfully but the container contains no video stream (self._container.streams.video is empty). This happens with audio-only media, some corrupt files, or text files passed as video; the fallback refuses to continue with an empty stream.

Source

Thrown at src/supervision/_cv2/_video.py:80

    def __init__(self, source: str | os.PathLike[str] | int) -> None:
        """Open a file source and retain a lazy PyAV frame iterator."""
        self._container: Any = None
        self._stream: Any = None
        self._frames: Iterator[Any] | None = None
        self._source = source
        self._position = 0
        self._frame_count_cache: int | None = None
        self._opened = False
        self._error: Exception | None = None

        try:
            if isinstance(source, int):
                raise BackendUnavailableError(
                    "PyAV fallback supports file paths, not webcam device indexes."
                )
            self._container = av.open(str(source), mode="r")
            if not self._container.streams.video:
                raise ValueError(f"Video source has no video stream: {source}")
            self._stream = self._container.streams.video[0]
            self._frames = iter(self._container.decode(video=self._stream.index))
            self._opened = True
        except Exception as exc:
            self._error = exc
            logger.warning("Failed to open video source %r: %s", source, exc)
            self.release()

    def isOpened(self) -> bool:
        """Return whether the underlying video file is open for reading."""
        return self._opened

    def _frame_count(self) -> int:
        """Return the stream count, decoding a second handle if metadata lacks it."""
        if self._frame_count_cache is not None:
            return self._frame_count_cache

        count = int(getattr(self._stream, "frames", 0) or 0)

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Verify the file is a video before opening: check extension and/or probe with av.open in a try block, inspecting container.streams.video.
  2. Re-download or re-mux corrupt files (ffmpeg -i in.mp4 -c copy out.mp4 often repairs truncation).
  3. Handle open failure via cap.isOpened() == False and skip/log the source instead of crashing a batch job.

Example fix

# before
cap = cv2.VideoCapture(user_supplied_path)  # may be audio-only

# after
import av
cap = cv2.VideoCapture(user_supplied_path)
if not cap.isOpened():
    logger.warning('not a readable video: %s', user_supplied_path)
    continue
Defensive patterns

Strategy: validation

Validate before calling

import av

def has_video_stream(path: str) -> bool:
    try:
        with av.open(path):
            return True
    except av.AVError:
        return False

if has_video_stream(path):
    cap = cv2.VideoCapture(path)

Try / catch

cap = cv2.VideoCapture(path)
if not cap.isOpened():
    logger.warning('skipping unreadable/non-video source: %s', path)
    return None

Prevention

When it happens

Trigger: cv2.VideoCapture('audio.mp3'), a truncated/corrupt video, an image or arbitrary binary renamed to .mp4, or a stream URL that resolves to audio-only content.

Common situations: Globbing a directory and opening every file without extension checks, user-uploaded media of unknown type, or partially downloaded videos where the moov/index is missing.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/b05c0bc2cd0a7580. Report an issue: GitHub.