{"record":{"id":"ac70c886d19900eb","repo":"herdrdev/herdr","slug":"ssh-install-stdin-missing","errorCode":null,"errorMessage":"ssh install stdin missing","messagePattern":"ssh install stdin missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/remote/attach.rs","lineNumber":511,"sourceCode":"        }\n        let (tmp_path, dest_path) = parse_remote_install_paths(&output.stdout)?;\n\n        let mut child = self\n            .command()\n            .arg(remote_install_stream_command(&tmp_path))\n            .stdin(Stdio::piped())\n            .stdout(Stdio::null())\n            .stderr(Stdio::inherit())\n            .spawn()\n            .map_err(|err| {\n                io::Error::new(err.kind(), format!(\"failed to start ssh install: {err}\"))\n            })?;\n\n        let mut source = File::open(source_path)?;\n        let copy_result = if let Some(mut stdin) = child.stdin.take() {\n            io::copy(&mut source, &mut stdin).map(|_| ())\n        } else {\n            Err(io::Error::new(\n                io::ErrorKind::BrokenPipe,\n                \"ssh install stdin missing\",\n            ))\n        };\n        let status = child.wait()?;\n        copy_result?;\n\n        if status.success() {\n            let output = self.sh_output(&remote_install_commit_script(&tmp_path, &dest_path))?;\n            if output.status.success() {\n                Ok(())\n            } else {\n                Err(command_failed(\"remote install commit failed\", &output))\n            }\n        } else {\n            Err(io::Error::other(format!(\n                \"remote install exited with {status}\"\n            )))","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/herdrdev/herdr/blob/f457cff4f2648eee85d176f8a41861241d4e8428/src/remote/attach.rs#L493-L529","documentation":"After successfully spawning the ssh install process with stdin piped, install_herdr streams the local Herdr binary into the child's stdin via io::copy; if child.stdin is None at that point, it returns ErrorKind::BrokenPipe 'ssh install stdin missing'. This is a defensive guard for the piped-stdin contract of the spawn.","triggerScenarios":"The ssh install child returned by spawn lacks a stdin handle despite .stdin(Stdio::piped()) being set — a spawn configuration regression or stdio setup failure in install_herdr.","commonSituations":"Code refactoring that removes or reorders .stdin(Stdio::piped()) in the install spawn builder; essentially a programming error rather than an environmental condition.","solutions":["Confirm the install Command builder sets .stdin(Stdio::piped()) before .spawn()","Add a unit test asserting the spawned install child always exposes a stdin handle","If hit at runtime, report it as a bug with the Herdr version and platform","Check for accidental take() of stdin earlier in the same function"],"exampleFix":"// before\nlet mut child = install_cmd.stdout(Stdio::null()).stderr(Stdio::inherit()).spawn()?;\n\n// after\nlet mut child = install_cmd.stdin(Stdio::piped())\n    .stdout(Stdio::null())\n    .stderr(Stdio::inherit())\n    .spawn()?;","handlingStrategy":"try-catch","validationCode":"// Verify the install spawn keeps piped stdin before copying\n// (builder-level check; nothing runtime-callable by users)","typeGuard":null,"tryCatchPattern":"match install_herdr(source, remote) {\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { /* stdio contract broken; verify version and report bug */ }\n    other => other?,\n}","preventionTips":["Keep .stdin(Stdio::piped()) in the install spawn builder","Add tests asserting the spawned install child has a stdin handle","Report occurrences with Herdr version — this indicates an internal regression"],"tags":["ssh","remote-install","stdio","spawn","broken-pipe"],"backgroundTag":"spawned-process-stdin-missing","analyzedSha":"f457cff4f2648eee85d176f8a41861241d4e8428","analyzedAt":"2026-08-28T15:41:09.197Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}