jdx/mise · error
a process it started kept its output open
Error message
a process it started kept its output open
What it means
After the shell exits, run_with_limits waits only output_grace for the output pipe to close. A descendant process that outlived the shell and still holds the stdout pipe is not considered the shell's answer; mise kills it and bails with 'a process it started kept its output open'. This guards against background children hijacking or indefinitely extending the command's output.
Solutions
- Redirect background processes' stdout/stderr away from the describe command's output (e.g. `> /dev/null 2>&1` or to a log file)
- Ensure the describe command waits for or terminates its children before exiting
- If a legitimately slow writer is expected, raise the output grace period in the history settings
Example fix
# before: child keeps the pipe open some-daemon & echo done # after: detach the child's output some-daemon > /dev/null 2>&1 & echo done
Defensive patterns
Strategy: validation
Validate before calling
# check the script does not leave stdout-holding children behind describe-command; sleep 2; ls /proc/*/fd 2>/dev/null | grep -c pipe || true # or run: timeout 5 describe-command >/dev/null && pgrep -f child-daemon
Prevention
- Redirect every background process's output to a file or /dev/null
- Have describe scripts clean up or wait for their children
- Avoid launching daemons/watchers from describe commands
When it happens
Trigger: The describe command's shell exits but spawns a background/detached child that inherits stdout and keeps it open longer than the output grace period (e.g. `cmd &`, daemons, watchers).
Common situations: Describe scripts that background long-running helpers (log tailers, dev servers); `nohup`-style launches inside the describe command; shells leaving stray children on abnormal exit.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- command output pipes did not close within
- description output exceeded
- exited with
- failed to read command
- took longer than s
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/4ad3fcb36fc50b4d.
Report an issue: GitHub.
Appendix: source
Thrown at src/system/history/describe_command.rs:176
let status = loop {
if let Some(status) = child.try_wait()? {
break status;
}
if started.elapsed() >= timeout {
// the shell and whatever it started; the reader thread ends
// with the last writer of the pipe, so it is not waited for
active.0.kill();
let _ = child.kill();
let _ = child.wait();
bail!("took longer than {}s", timeout.as_secs());
}
std::thread::sleep(Duration::from_millis(100));
};
// a descendant that outlived the shell and kept the pipe is not the
// shell's answer: the output is waited for a moment, not forever
let Ok(output) = receiver.recv_timeout(output_grace) else {
active.0.kill();
bail!("a process it started kept its output open");
};
if output.len() > DIFF_LIMIT {
bail!("description output exceeded {} bytes", DIFF_LIMIT);
}
if !status.success() {
bail!("exited with {status}");
}
let Some(line) = first_line(&output) else {
return Ok(None);
};
annotate(
store,
entry,
Annotation {
description: Some(line.clone()),
description_source: Some(DescriptionSource::Command),
labels: None,
updated_at: store::now_rfc3339(),View on GitHub (pinned to 533346cc37)