jdx/mise · error
command output pipes did not close within {pipe_drain_timeou
Error message
command output pipes did not close within {pipe_drain_timeout:?} What it means
After the child process exited, its output pipes did not close within pipe_drain_timeout — some descendant inherited the pipe and kept it open. mise waits until the drain deadline, then kills the whole process tree (SIGKILL on unix, kill_process_tree on Windows) and reports `command output pipes did not close within <duration>`.
Source
Thrown at src/cmd.rs:1432
#[cfg(unix)]
signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);
#[cfg(windows)]
kill_process_tree(id);
let _ = wait.await;
return Err(err);
}
}
}
}
let drain_deadline = Instant::now() + pipe_drain_timeout;
loop {
let remaining = drain_deadline.saturating_duration_since(Instant::now());
if remaining.is_zero() {
#[cfg(unix)]
signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);
#[cfg(windows)]
kill_process_tree(id);
bail!("command output pipes did not close within {pipe_drain_timeout:?}");
}
let output = match tokio::time::timeout(remaining, rx.recv()).await {
Ok(Some(output)) => output,
Ok(None) => break,
Err(_) => {
#[cfg(unix)]
signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);
#[cfg(windows)]
kill_process_tree(id);
bail!("command output pipes did not close within {pipe_drain_timeout:?}");
}
};
if let Err(err) = consume(output) {
#[cfg(unix)]
signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);
#[cfg(windows)]
kill_process_tree(id);
return Err(err);View on GitHub (pinned to afd2eddd3a)
Solutions
- Make the task's spawned background processes detach or close inherited stdio (e.g. `daemon >log 2>&1 </dev/null &`).
- Don't leave dev servers/watchers running at the end of a task; kill them explicitly in the task script.
- Increase pipe_drain_timeout if the remaining writers legitimately need more time.
- Use setsid/nohup or redirect all three fds so the grandchild no longer holds the pipe.
Example fix
// before (task script) npm run dev & build && test # pipes never close; killed after drain timeout // after nohup npm run dev >server.log 2>&1 < /dev/null & build && test && kill $!
Defensive patterns
Strategy: validation
Validate before calling
# ensure no descendants will hold the pipes open script -c 'setsid bash -c "your-daemon >log 2>&1 </dev/null &"' /dev/null
Try / catch
if mise run task 2>&1 | grep -q 'pipes did not close'; then kill_leftover_daemons; fi
Prevention
- Redirect stdin/stdout/stderr of any backgrounded process to files or /dev/null.
- Explicitly kill dev servers/watchers at the end of task scripts.
- Never nohup background jobs without redirecting all three fds inside mise tasks.
When it happens
Trigger: execute_hashes_async: the main child exited but rx never got EOF within pipe_drain_timeout because a grandchild/daemon holds stdout/stderr open; the deadline is reached and the tree is killed.
Common situations: Tasks that spawn background daemons/servers (dev servers, watchers) which inherit stdio; scripts launching nohup'd or detached grandchildren; test runners leaving orphaned child processes.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timed out after {timeout:?}
- a process it started kept its output open
- --connect-timeout must be greater than zero
- timed out after {duration:?}
- failed to read command {stream}: {err}
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/c6dba2ce5504e0e8.
Report an issue: GitHub.