roboflow/supervision · error · RuntimeError

write_frame requires an open VideoSink context.

Error message

write_frame requires an open VideoSink context.

What it means

Raised by VideoSink.write_frame() in supervision.utils.video when it is called while the internal cv2.VideoWriter is None — i.e. outside the `with sv.VideoSink(...)` context. The writer is created in __enter__ and released (set to None) in __exit__; writing outside that window would silently drop frames, so the invariant is enforced with a RuntimeError.

Source

Thrown at src/supervision/utils/video.py:155

        )
        # OpenCV can construct a writer object that is not usable for the target path.
        if not self.__writer.isOpened():
            self.__writer.release()
            self.__writer = None
            raise RuntimeError(f"Could not open video writer for {self.target_path}")
        return self

    def write_frame(self, frame: npt.NDArray[np.uint8]) -> None:
        """
        Writes a single video frame to the target video file.

        Args:
            frame: The video frame to be written to the file. The frame
                must be in BGR color format.
        """
        # Preserve the context-manager invariant instead of silently dropping frames.
        if self.__writer is None:
            raise RuntimeError("write_frame requires an open VideoSink context.")
        self.__writer.write(frame)

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_value: BaseException | None,
        exc_traceback: TracebackType | None,
    ) -> None:
        """Release the underlying video writer when leaving the context."""
        if self.__writer is not None:
            self.__writer.release()
            self.__writer = None


def _validate_and_setup_video(
    source_path: str, start: int, end: int | None, iterative_seek: bool = False
) -> tuple[cv2.VideoCapture, int, int]:
    video = cv2.VideoCapture(source_path)

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Move all write_frame calls inside the with-block: with sv.VideoSink(...) as sink: sink.write_frame(f).
  2. Do not cache the sink beyond the context; create a new one for each output file.
  3. If an exception hit inside the block, handle it there — after __exit__ the sink is closed by design.

Example fix

// before
sink = sv.VideoSink('out.mp4', info)
sink.write_frame(frame)  # not opened

// after
with sv.VideoSink('out.mp4', info) as sink:
    sink.write_frame(frame)
Defensive patterns

Strategy: validation

Validate before calling

# write only inside the context; nothing to pre-check beyond structure
with sv.VideoSink(target_path, info) as sink:
    for frame in frames:
        sink.write_frame(frame)  # always inside the with-block

Try / catch

try:
    sink.write_frame(frame)
except RuntimeError as e:
    if 'open VideoSink context' in str(e):
        raise RuntimeError('write_frame called outside with sv.VideoSink(...)') from e
    raise

Prevention

When it happens

Trigger: Calling sink.write_frame(frame) before entering the context manager (e.g. after constructing VideoSink but before `with`); storing the sink and writing after the with-block exited; exception inside the with-body prematurely triggering __exit__ then code continuing to write.

Common situations: Refactoring loops out of the with-block but keeping the sink reference; a callback registered with sv.process_video outliving the sink; error paths that swallow the original exception and keep processing frames.

Related errors


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