jdx/mise · error

command wait must complete

Error message

command wait must complete

What it means

`execute_hashes_async_with_drain_timeout` spawns a child command and waits on it, keeping the `status` in an `Option` consumed by the timeout/drain logic. Once the wait future resolves — regardless of branch — a status must exist; this `.expect()` panics if the code reached the unwrap point without the wait ever completing. It is a control-flow invariant guard on the child-process wait path.

Solutions

  1. Report the panic with `MISE_DEBUG=1` and the failing command to the mise repo
  2. Restructure so the child's status is bound directly from the awaited `wait()`/`wait_with_output()` result instead of an `Option` filled in multiple branches
  3. Verify the timeout branch still awaits/consumes the child before falling through
  4. Run the relevant unit tests in src/cmd.rs to exercise all timeout and non-timeout branches

Example fix

// before
let status = status.expect("command wait must complete");
// after
let Some(status) = status else {
    bail!("internal error: command wait did not complete");
};
Defensive patterns

Strategy: validation

Validate before calling

// Callers of cmd execution helpers: prefer APIs returning Result
// and avoid wrapping child waits in Option-filled branches
let status = cmd.status().await?; // propagate instead of unwrapping later

Prevention

When it happens

Trigger: A logic bug where every branch that reads `status` is not actually preceded by awaiting the child's wait future; modifications to the timeout/drain structure that skip the awaited wait on some path; cancellation of the wait future without aborting the surrounding logic.

Common situations: Contributors refactoring the drain-timeout machinery in src/cmd.rs; adding a new early-return around the child wait that leaves `status` unset while still falling through to the status check.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/cmd.rs:1457

                    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);
            }
        }

        if let Some(guard) = &timeout_guard {
            guard.cancel();
        }
        let status = status.expect("command wait must complete");
        if !status.success() {
            if let Some(timeout) = timeout_guard.as_ref().and_then(|guard| guard.timed_out()) {
                bail!("timed out after {timeout:?}");
            }
            bail!("exited with non-zero status: {status}");
        }
        Ok((
            stdout_hasher.finalize().to_hex().to_string(),
            stderr_hasher.finalize().to_hex().to_string(),
        ))
    }

    /// Run the command and return stdout, even when raw mode is enabled.
    pub(crate) async fn read(mut self) -> Result<String> {
        let _read_lock = RAW_LOCK.read().await;
        debug!("$ {self}");
        self.cmd.kill_on_drop(true);
        #[cfg(unix)]

View on GitHub (pinned to afd2eddd3a)