{"record":{"id":"d800abd206095686","repo":"jdx/mise","slug":"command-wait-must-complete","errorCode":null,"errorMessage":"command wait must complete","messagePattern":"command wait must complete","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd.rs","lineNumber":1457,"sourceCode":"                    signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);\n                    #[cfg(windows)]\n                    kill_process_tree(id);\n                    bail!(\"command output pipes did not close within {pipe_drain_timeout:?}\");\n                }\n            };\n            if let Err(err) = consume(output) {\n                #[cfg(unix)]\n                signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);\n                #[cfg(windows)]\n                kill_process_tree(id);\n                return Err(err);\n            }\n        }\n\n        if let Some(guard) = &timeout_guard {\n            guard.cancel();\n        }\n        let status = status.expect(\"command wait must complete\");\n        if !status.success() {\n            if let Some(timeout) = timeout_guard.as_ref().and_then(|guard| guard.timed_out()) {\n                bail!(\"timed out after {timeout:?}\");\n            }\n            bail!(\"exited with non-zero status: {status}\");\n        }\n        Ok((\n            stdout_hasher.finalize().to_hex().to_string(),\n            stderr_hasher.finalize().to_hex().to_string(),\n        ))\n    }\n\n    /// Run the command and return stdout, even when raw mode is enabled.\n    pub(crate) async fn read(mut self) -> Result<String> {\n        let _read_lock = RAW_LOCK.read().await;\n        debug!(\"$ {self}\");\n        self.cmd.kill_on_drop(true);\n        #[cfg(unix)]","sourceCodeStart":1439,"sourceCodeEnd":1475,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/cmd.rs#L1439-L1475","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Report the panic with `MISE_DEBUG=1` and the failing command to the mise repo","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","Verify the timeout branch still awaits/consumes the child before falling through","Run the relevant unit tests in src/cmd.rs to exercise all timeout and non-timeout branches"],"exampleFix":"// before\nlet status = status.expect(\"command wait must complete\");\n// after\nlet Some(status) = status else {\n    bail!(\"internal error: command wait did not complete\");\n};","handlingStrategy":"validation","validationCode":"// Callers of cmd execution helpers: prefer APIs returning Result\n// and avoid wrapping child waits in Option-filled branches\nlet status = cmd.status().await?; // propagate instead of unwrapping later","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Bind child status directly from the awaited wait call","Keep timeout/drain logic in one function per command type","Treat occurrences as mise bugs; report with a debug trace"],"tags":["rust","panic","internal-invariant","process"],"backgroundTag":"internal-invariant-violation","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}