{"record":{"id":"174e0a98591b900a","repo":"roboflow/supervision","slug":"write-frame-requires-an-open-videosink-context","errorCode":null,"errorMessage":"write_frame requires an open VideoSink context.","messagePattern":"write_frame requires an open VideoSink context\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/supervision/utils/video.py","lineNumber":155,"sourceCode":"        )\n        # OpenCV can construct a writer object that is not usable for the target path.\n        if not self.__writer.isOpened():\n            self.__writer.release()\n            self.__writer = None\n            raise RuntimeError(f\"Could not open video writer for {self.target_path}\")\n        return self\n\n    def write_frame(self, frame: npt.NDArray[np.uint8]) -> None:\n        \"\"\"\n        Writes a single video frame to the target video file.\n\n        Args:\n            frame: The video frame to be written to the file. The frame\n                must be in BGR color format.\n        \"\"\"\n        # Preserve the context-manager invariant instead of silently dropping frames.\n        if self.__writer is None:\n            raise RuntimeError(\"write_frame requires an open VideoSink context.\")\n        self.__writer.write(frame)\n\n    def __exit__(\n        self,\n        exc_type: type[BaseException] | None,\n        exc_value: BaseException | None,\n        exc_traceback: TracebackType | None,\n    ) -> None:\n        \"\"\"Release the underlying video writer when leaving the context.\"\"\"\n        if self.__writer is not None:\n            self.__writer.release()\n            self.__writer = None\n\n\ndef _validate_and_setup_video(\n    source_path: str, start: int, end: int | None, iterative_seek: bool = False\n) -> tuple[cv2.VideoCapture, int, int]:\n    video = cv2.VideoCapture(source_path)","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/utils/video.py#L137-L173","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Move all write_frame calls inside the with-block: with sv.VideoSink(...) as sink: sink.write_frame(f).","Do not cache the sink beyond the context; create a new one for each output file.","If an exception hit inside the block, handle it there — after __exit__ the sink is closed by design."],"exampleFix":"// before\nsink = sv.VideoSink('out.mp4', info)\nsink.write_frame(frame)  # not opened\n\n// after\nwith sv.VideoSink('out.mp4', info) as sink:\n    sink.write_frame(frame)","handlingStrategy":"validation","validationCode":"# write only inside the context; nothing to pre-check beyond structure\nwith sv.VideoSink(target_path, info) as sink:\n    for frame in frames:\n        sink.write_frame(frame)  # always inside the with-block","typeGuard":null,"tryCatchPattern":"try:\n    sink.write_frame(frame)\nexcept RuntimeError as e:\n    if 'open VideoSink context' in str(e):\n        raise RuntimeError('write_frame called outside with sv.VideoSink(...)') from e\n    raise","preventionTips":["Keep sink usage lexically inside the `with` block; never leak the sink reference out.","In callbacks, pass the sink as a parameter scoped to the context, not a global."],"tags":["video","context-manager","lifecycle","api-misuse"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}