BloopAI/vibe-kanban · error

Failed to take child stdout

Error message

Failed to take child stdout

What it means

Same as the stdin variant: after spawning the child, spawn_stdio_session takes the child's stdout handle and fails if it is None. Stdout is None only when the command was not configured with Stdio::piped() for stdout, but the stdio session needs to relay the child's output back over the SSH channel.

Source

Thrown at crates/embedded-ssh/src/handler.rs:102

        for (k, v) in env {
            cmd.env(k, v);
        }
        if let Ok(home) = std::env::var("HOME") {
            cmd.current_dir(home);
        }

        let mut child = cmd
            .spawn()
            .map_err(|e| anyhow::anyhow!("Failed to spawn stdio command: {e}"))?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| anyhow::anyhow!("Failed to take child stdin"))?;
        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| anyhow::anyhow!("Failed to take child stdout"))?;
        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| anyhow::anyhow!("Failed to take child stderr"))?;

        let (writer_tx, mut writer_rx) = mpsc::channel::<Vec<u8>>(64);
        tokio::spawn(async move {
            let mut stdin = stdin;
            while let Some(data) = writer_rx.recv().await {
                if stdin.write_all(&data).await.is_err() {
                    break;
                }
                if stdin.flush().await.is_err() {
                    break;
                }
            }
        });

View on GitHub (pinned to 4deb7eca8f)

Solutions

  1. Add/restore .stdout(Stdio::piped()) on the command before spawn.
  2. Verify no code path replaces stdout with Stdio::inherit or Stdio::null for stdio sessions.
  3. Add a unit/integration test that spawns a trivial command through spawn_stdio_session and asserts output is relayed, catching regressions.

Example fix

// before
cmd.stdout(Stdio::inherit());
// after
cmd.stdout(Stdio::piped());
Defensive patterns

Strategy: validation

Validate before calling

let mut cmd = Command::new(shell);
cmd.stdin(Stdio::piped());
cmd.stdout(Stdio::piped()); // required, otherwise take() -> None
let mut child = cmd.spawn()?;

Prevention

When it happens

Trigger: child.stdout.take() returns None because the spawned command lacks .stdout(Stdio::piped()) — an internal construction bug, not user input.

Common situations: Encountered only after code changes to command construction in the embedded-ssh handler; users cannot cause it remotely.

Related errors


AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29). Data as JSON: /api/errors/0d028e6dcab2f547. Report an issue: GitHub.