{"record":{"id":"244053dc46729411","repo":"jdx/mise","slug":"piped-stdin-is-available","errorCode":null,"errorMessage":"piped stdin is available","messagePattern":"piped stdin is available","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/sudo.rs","lineNumber":234,"sourceCode":"    let argv = argv(program, args);\n    let manual_cmd = std::iter::once(\"sudo\".to_string())\n        .chain(std::iter::once(program.to_string()))\n        .chain(args.iter().cloned())\n        .collect::<Vec<_>>()\n        .join(\" \");\n    ensure_elevation_available(&manual_cmd)?;\n    let _progress_pause = pause_progress_for_child();\n    info!(\"$ {}\", argv.join(\" \"));\n    let mut child = Command::new(&argv[0])\n        .args(&argv[1..])\n        .stdin(Stdio::piped())\n        .stdout(Stdio::inherit())\n        .stderr(Stdio::inherit())\n        .spawn()?;\n    child\n        .stdin\n        .take()\n        .expect(\"piped stdin is available\")\n        .write_all(input)?;\n    let status = child.wait()?;\n    if !status.success() {\n        bail!(\"elevated bootstrap helper failed with {status}\");\n    }\n    Ok(())\n}\n\n/// Run one elevated helper with a private stdin payload and capture stdout.\n/// Stderr remains attached to the terminal for sudo prompts and diagnostics.\npub(crate) fn run_with_input_output(\n    program: &str,\n    args: &[String],\n    input: &[u8],\n) -> Result<Vec<u8>> {\n    let argv = argv(program, args);\n    let manual_cmd = std::iter::once(\"sudo\".to_string())\n        .chain(std::iter::once(program.to_string()))","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/sudo.rs#L216-L252","documentation":"A Rust panic when writing input to a spawned elevated helper: the child process was configured with piped stdin implicitly via Stdio::piped() (or default piping in this builder), and the code expects child.stdin to be Some when taking it. It fires only if the spawn configuration was changed so stdin is not piped (e.g. Stdio::inherit() or null), making the take() return None.","triggerScenarios":"Running run_with_input (elevated bootstrap helper with input) after someone changed the Command's stdin configuration away from piped; on stock code it cannot fire.","commonSituations":"Development edits to the sudo/bootstrap Command builder; custom builds where stdin was switched to inherit and input is still written.","solutions":["Ensure the Command is built with .stdin(Stdio::piped()) before spawn in run_with_input","Check no platform-specific builder overrides stdin on this OS","If input writing can fail, handle io::Error from write_all instead of relying on the expect","Report a bug with OS and command if hit in an unmodified build"],"exampleFix":"// before\n.stdout(Stdio::inherit())\n.stderr(Stdio::inherit())\n// after\n.stdin(Stdio::piped())\n.stdout(Stdio::inherit())\n.stderr(Stdio::inherit())","handlingStrategy":"validation","validationCode":"if !cmd_builder_stdin_is_piped { return Err(anyhow!(\"helper stdin not piped\")); }","typeGuard":"fn stdin_piped(child: &Child) -> Option<&mut ChildStdin> { /* use child.stdin.take() result */ None.or(child.stdin.as_mut()) }","tryCatchPattern":"match child.stdin.take() {\n    Some(mut stdin) => stdin.write_all(input)?,\n    None => bail!(\"internal error: helper stdin was not piped\"),\n}","preventionTips":["Always set .stdin(Stdio::piped()) when spawning with input","Keep platform-specific Command builders in sync","Replace expect with a Result-returning error for spawn config mistakes"],"tags":["rust","panic","process-spawn","stdin"],"backgroundTag":"internal-invariant-violation","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}