{"record":{"id":"dd5f6b282d680f88","repo":"BloopAI/vibe-kanban","slug":"failed-to-take-child-stdin","errorCode":null,"errorMessage":"Failed to take child stdin","messagePattern":"Failed to take child stdin","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/embedded-ssh/src/handler.rs","lineNumber":98,"sourceCode":"        cmd.stdin(Stdio::piped());\n        cmd.stdout(Stdio::piped());\n        cmd.stderr(Stdio::piped());\n        cmd.env(\"TERM\", \"xterm-256color\");\n        for (k, v) in env {\n            cmd.env(k, v);\n        }\n        if let Ok(home) = std::env::var(\"HOME\") {\n            cmd.current_dir(home);\n        }\n\n        let mut child = cmd\n            .spawn()\n            .map_err(|e| anyhow::anyhow!(\"Failed to spawn stdio command: {e}\"))?;\n\n        let stdin = child\n            .stdin\n            .take()\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to take child stdin\"))?;\n        let stdout = child\n            .stdout\n            .take()\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to take child stdout\"))?;\n        let stderr = child\n            .stderr\n            .take()\n            .ok_or_else(|| anyhow::anyhow!(\"Failed to take child stderr\"))?;\n\n        let (writer_tx, mut writer_rx) = mpsc::channel::<Vec<u8>>(64);\n        tokio::spawn(async move {\n            let mut stdin = stdin;\n            while let Some(data) = writer_rx.recv().await {\n                if stdin.write_all(&data).await.is_err() {\n                    break;\n                }\n                if stdin.flush().await.is_err() {\n                    break;","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/embedded-ssh/src/handler.rs#L80-L116","documentation":"Immediately after a successful spawn, the code takes ownership of the child's stdin handle. std::process::Child::stdin returns None only if stdin was not piped (stdin(Stdio::piped()) was not set when building the command). This error is therefore an internal invariant violation: the command was constructed without piped stdin while the stdio session requires it.","triggerScenarios":"spawn_stdio_session's command builder omits .stdin(Stdio::piped()) (or a code change replaced it), so child.stdin.take() returns None even though spawn succeeded.","commonSituations":"Practically only seen after modifying the handler's command construction or when a refactor changed which Stdio configuration is applied; end users cannot trigger it via SSH requests.","solutions":["Ensure the command is built with .stdin(Stdio::piped()) before spawn in spawn_stdio_session.","Confirm no later code overrides stdin (e.g. Stdio::inherit/null) after it was piped.","If using tokio::process, verify you're taking stdin before the child is polled to completion (handles aren't auto-closed here since it's std, but keep ordering sane)."],"exampleFix":"// before\nlet mut cmd = Command::new(shell);\ncmd.stdin(Stdio::null());\n// after\nlet mut cmd = Command::new(shell);\ncmd.stdin(Stdio::piped());\ncmd.stdout(Stdio::piped());\ncmd.stderr(Stdio::piped());","handlingStrategy":"validation","validationCode":"// assert construction configures piped stdio (in handler tests)\nlet mut cmd = Command::new(shell);\nassert_piped_stdio(&mut cmd);\nfn assert_piped_stdio(cmd: &mut Command) {\n    cmd.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always configure Stdio::piped() for stdin, stdout, and stderr when building stdio-session commands.","Add a test that runs spawn_stdio_session on a trivial command to catch construction regressions.","Don't share command-construction helpers with PTY paths that use different Stdio settings.","Treat any of the three 'Failed to take child *' errors as internal bugs — fix construction, not callers."],"tags":["process-spawn","ssh","invariant-violation"],"backgroundTag":"missing-piped-stdio","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}