roboflow/supervision · error · ValueError

Video frames must use uint8 dtype

Error message

Video frames must use uint8 dtype

What it means

The PyAV writer wraps frames with av.VideoFrame.from_ndarray(..., format='bgr24'), which requires uint8 data. Frames in float32/float64 (normalized 0-1 images, model outputs) or uint16 are rejected before encoding.

Source

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

        except Exception as exc:
            self._error = exc
            self.release()

    def isOpened(self) -> bool:
        """Return whether the writer initialized successfully."""
        return self._opened

    def write(self, frame: npt.NDArray[np.uint8]) -> None:
        """Encode one BGR frame and mux all packets produced by the encoder."""
        if not self._opened or self._container is None or self._stream is None:
            raise RuntimeError("Video writer is not open") from self._error
        if frame.shape != (self._height, self._width, 3):
            raise ValueError(
                "Video frame must have shape "
                f"({self._height}, {self._width}, 3), got {frame.shape}"
            )
        if frame.dtype != np.uint8:
            raise ValueError("Video frames must use uint8 dtype")

        video_frame = av.VideoFrame.from_ndarray(
            np.ascontiguousarray(frame), format="bgr24"
        )
        for packet in self._stream.encode(video_frame):
            self._container.mux(packet)

    def release(self) -> None:
        """Flush delayed encoder packets and close the output container."""
        container = self._container
        stream = self._stream
        self._container = None
        self._stream = None
        self._opened = False
        if container is None:
            return
        try:
            if stream is not None:

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Convert before writing: frame_u8 = np.clip(frame * 255, 0, 255).astype(np.uint8).
  2. If already in 0-255 floats: frame_u8 = frame.astype(np.uint8).
  3. Keep a single uint8 annotation canvas instead of converting model tensors directly.

Example fix

# before
writer.write(heatmap_float)  # float64 in [0, 1]

# after
frame_u8 = (np.clip(heatmap_float, 0, 1) * 255).astype(np.uint8)
writer.write(np.stack([frame_u8]*3, axis=-1))
Defensive patterns

Strategy: validation

Validate before calling

if frame.dtype != np.uint8:
    frame = np.clip(frame, 0, 255).astype(np.uint8) if frame.max() > 1 else (np.clip(frame, 0, 1) * 255).astype(np.uint8)
writer.write(frame)

Type guard

def is_writable_frame(frame: np.ndarray) -> bool:
    return frame.dtype == np.uint8 and frame.ndim == 3 and frame.shape[2] == 3

Prevention

When it happens

Trigger: Writing float arrays such as normalized model outputs, occupancy heat maps, or images processed in float precision without converting back to uint8.

Common situations: Pipelines that normalize frames for inference and forget to de-normalize; heat-map visualizations computed in float; mixing annotated float arrays from matplotlib-like operations. Real OpenCV also misbehaves with non-uint8, but the fallback fails loudly.

Related errors


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