roboflow/supervision · error · ValueError

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

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.

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)

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.