From 8782704b0dbb1bd39f3887b808aacd10076a28a4 Mon Sep 17 00:00:00 2001 From: Misha Davidov Date: Mon, 4 Aug 2025 15:35:15 -0700 Subject: [PATCH] exec: timeout on grandchildren We were enforcing the 10 s wall-clock limit only on the child process. If that child (bash) spawns grandchildren and we kill it on timeout, those grandchildren still have the original stdout/err pipe open, so the background tasks that are draining the pipes block forever --- codex-rs/core/src/exec.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index c34d84b6ac..5793ccaacb 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -365,7 +365,7 @@ pub(crate) async fn consume_truncated_output( join_res = &mut *handle => { match join_res { Ok(io_res) => io_res, - Err(join_err) => Err(std::io::Error::new(std::io::ErrorKind::Other, join_err)), + Err(join_err) => Err(std::io::Error::other(join_err)), } }, _ = tokio::time::sleep(timeout) => { @@ -378,8 +378,16 @@ pub(crate) async fn consume_truncated_output( let mut stdout_handle = stdout_handle; let mut stderr_handle = stderr_handle; - let stdout = await_with_timeout(&mut stdout_handle, Duration::from_millis(IO_DRAIN_TIMEOUT_MS)).await?; - let stderr = await_with_timeout(&mut stderr_handle, Duration::from_millis(IO_DRAIN_TIMEOUT_MS)).await?; + let stdout = await_with_timeout( + &mut stdout_handle, + Duration::from_millis(IO_DRAIN_TIMEOUT_MS), + ) + .await?; + let stderr = await_with_timeout( + &mut stderr_handle, + Duration::from_millis(IO_DRAIN_TIMEOUT_MS), + ) + .await?; Ok(RawExecToolCallOutput { exit_status,