From 185a19e7bedd001f52686678023225c876971a2b Mon Sep 17 00:00:00 2001 From: abrichr Date: Thu, 23 Jul 2026 18:10:27 -0700 Subject: [PATCH] fix: report FFmpeg input worker startup failure --- openadapt_capture/video.py | 4 ++-- tests/test_video.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/openadapt_capture/video.py b/openadapt_capture/video.py index dd41f49..f2f1e6d 100644 --- a/openadapt_capture/video.py +++ b/openadapt_capture/video.py @@ -747,14 +747,14 @@ def _start(self) -> subprocess.Popen[bytes]: ) try: self._input_thread.start() - except BaseException: + except BaseException as exc: process.kill() process.wait() stderr_file.close() self._process = None self._stderr_file = None self._input_thread = None - raise + raise FFmpegEncodingError(f"Could not start FFmpeg input worker: {exc}") from exc return process def _stderr_detail(self) -> str: diff --git a/tests/test_video.py b/tests/test_video.py index 1dd56ee..4e6b010 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -567,6 +567,33 @@ def test_direct_encode_retries_partial_pipe_writes(tmp_path, monkeypatch): assert video._read_timing_box(output) == (Fraction(24), [(0, 0.0)]) +def test_direct_encode_worker_start_failure_reaps_process(tmp_path, monkeypatch): + executable = tmp_path / "ffmpeg" + executable.write_bytes(b"fake") + stage = video.FFmpegFrameStage( + tmp_path / "capture.mp4", + _small_stream(), + _provision(executable), + ) + process = _FakeProcess(stage._encode_command()) + _install_fake_popen(monkeypatch, process) + + def fail_start(_thread): + raise RuntimeError("thread resources unavailable") + + monkeypatch.setattr(video.threading.Thread, "start", fail_start) + + with pytest.raises( + video.FFmpegEncodingError, + match="Could not start FFmpeg input worker", + ): + stage.stage_frame(Image.new("RGB", (2, 1), "red"), 0) + assert process._killed is True + assert process.returncode == -9 + assert stage._process is None + assert stage._input_thread is None + + def test_direct_encode_zero_length_pipe_write_fails_loudly(tmp_path, monkeypatch): executable = tmp_path / "ffmpeg" executable.write_bytes(b"fake")