roboflow/supervision · error · ValueError

Video frame must have shape

Error message

Video frame must have shape ({self._height}, {self._width}, 3), got {frame.shape}

What it means

The PyAV writer validates every frame against the exact (height, width, 3) shape fixed at construction; av.VideoFrame.from_ndarray requires a matching bgr24 layout. Any frame of different resolution, channel count, or an accidentally transposed (W, H) array is rejected.

Solutions

  1. Construct the writer with (height, width) from an actual frame: frame_size=(frame.shape[1], frame.shape[0]) — note OpenCV's VideoWriter takes (width, height).
  2. Resize every frame to the writer's fixed size before write().
  3. Ensure frames are 3-channel BGR; convert grayscale with cv2.cvtColor.

Example fix

# before
writer = cv2.VideoWriter('out.mp4', fourcc, fps, (1080, 1920))  # swapped; frames are (1920,1080,3)

# after
writer = cv2.VideoWriter('out.mp4', fourcc, fps, (1920, 1080))
Defensive patterns

Strategy: validation

Validate before calling

h, w = frame.shape[:2]
writer = cv2.VideoWriter(path, fourcc, fps, (w, h))  # OpenCV order: (width, height)
assert frame.shape == (h, w, 3)

Prevention

When it happens

Trigger: Passing frames whose shape differs from the constructor's frame_size — e.g. writer created with (1920, 1080) while frames are 1080p after a resize, width/height swapped at construction, or 2D grayscale frames.

Common situations: The classic OpenCV bug of passing (width, height) instead of (height, width) to VideoWriter, annotators that change frame resolution mid-stream, or mixing sources (webcam + file) with one writer.

Related errors


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

Appendix: source

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

            self._stream = self._container.add_stream(codec, rate=rate)
            self._stream.width = self._width
            self._stream.height = self._height
            self._stream.pix_fmt = pixel_format
            self._opened = True
        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

View on GitHub (pinned to 7f254d9784)