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

  1. Make the task's spawned background processes detach or close inherited stdio (e.g. `daemon >log 2>&1 </dev/null &`).
  2. Don't leave dev servers/watchers running at the end of a task; kill them explicitly in the task script.
  3. Increase pipe_drain_timeout if the remaining writers legitimately need more time.
  4. 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

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

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/c6dba2ce5504e0e8. Report an issue: GitHub.