{"record":{"id":"a56059c1cb829eb7","repo":"jdx/mise","slug":"stdout-must-be-piped","errorCode":null,"errorMessage":"stdout must be piped","messagePattern":"stdout must be piped","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd.rs","lineNumber":1561,"sourceCode":"                    );\n                    Ok(())\n                });\n            }\n        }\n        let mut cp = self\n            .spawn_async_with_etxtbsy_retry()\n            .await\n            .wrap_err_with(|| format!(\"failed to execute command: {self}\"))?;\n        let id = cp.id().unwrap_or_default();\n        let _running_pid = RunningPidGuard::new(cp.id());\n        if let Some(text) = self.stdin.take()\n            && let Some(mut stdin) = cp.stdin.take()\n        {\n            tokio::spawn(async move {\n                let _ = stdin.write_all(text.as_bytes()).await;\n            });\n        }\n        let stdout = cp.stdout.take().expect(\"stdout must be piped\");\n        let stderr = cp.stderr.take().expect(\"stderr must be piped\");\n        let stdout_task = tokio::spawn(read_capped(stdout, max_output_bytes));\n        let stderr_task = tokio::spawn(read_capped(stderr, max_output_bytes));\n        let status = match self.timeout {\n            Some(timeout) => match tokio::time::timeout(timeout, cp.wait()).await {\n                Ok(status) => status?,\n                Err(_) => {\n                    #[cfg(unix)]\n                    signal_process_tree(id, nix::sys::signal::Signal::SIGKILL);\n                    #[cfg(windows)]\n                    kill_process_tree(id);\n                    let _ = cp.wait().await;\n                    bail!(\"timed out after {timeout:?}\");\n                }\n            },\n            None => cp.wait().await?,\n        };\n        let (stdout, stdout_len) = stdout_task.await??;","sourceCodeStart":1543,"sourceCodeEnd":1579,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/cmd.rs#L1543-L1579","documentation":"`read_bounded` captures a child command's output, which requires stdout/stderr to have been configured with `Stdio::piped()` at spawn time. This `.expect(\"stdout must be piped\")` panics if `child.stdout` is `None`, meaning the spawn configuration and the reader drifted apart. It enforces the contract that any command built for `read_bounded` pipes both streams.","triggerScenarios":"A `CmdWrapper`/command built with `stdout(Stdio::inherit())`, `null()`, or default (inherit) stdio and then passed to `read_bounded`; a refactor changing spawn configuration; a helper that conditionally overrides stdout.","commonSituations":"Contributors add a new caller of `read_bounded` that reuses a command configured for interactive use; a global setting or wrapper mutates stdio before spawn.","solutions":["Ensure the command sets `.stdout(Stdio::piped())` before spawning (and `.stderr(Stdio::piped())` for the sibling call)","Trace which caller constructed the command and add piping there, not in `read_bounded`","Report with `MISE_DEBUG=1` if this occurs on an unmodified mise build","Add an assertion at spawn time so misconfigured commands fail fast with a clear message"],"exampleFix":"// before\nlet cmd = CmdWrapper::new(program); // inherits stdio\n// after\nlet mut cmd = CmdWrapper::new(program);\ncmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());","handlingStrategy":"validation","validationCode":"// Before handing a command to output-capturing helpers:\nassert_eq!(cmd.get_stdout(), Stdio::piped(), \"read_bounded requires piped stdout\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Configure piped stdio at command construction, once","Never reuse interactive command builders for capture paths","Add spawn-time assertions when introducing new execute helpers"],"tags":["rust","panic","internal-invariant","process","stdio"],"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-14T00:17:10.932Z"}