zeroclaw-labs/zeroclaw · error · anyhow::Error
No input received from stdin
Error message
No input received from stdin
What it means
interact_text_with_io writes the prompt, flushes, then reads one line from stdin. read_line returning 0 bytes is EOF (stream closed — not the same as an empty line), and the helper bails immediately. Note the configured .default() value only applies to a blank line; EOF never falls back to the default.
Source
Thrown at crates/zeroclaw-runtime/src/cli_input.rs:136
pub fn interact_text(self) -> Result<String> {
let stdin = std::io::stdin();
let stdout = std::io::stdout();
self.interact_text_with_io(stdin.lock(), stdout.lock())
}
fn interact_text_with_io<R: BufRead, W: Write>(
self,
mut reader: R,
mut writer: W,
) -> Result<String> {
loop {
write!(writer, "{}", self.render_prompt())?;
writer.flush()?;
let mut line = String::new();
let bytes_read = reader.read_line(&mut line)?;
if bytes_read == 0 {
bail!("No input received from stdin");
}
let trimmed = trim_trailing_line_ending(&line);
if trimmed.is_empty() {
if let Some(default) = &self.default {
return Ok(default.clone());
}
if self.allow_empty {
return Ok(String::new());
}
writeln!(writer, "Input cannot be empty.")?;
continue;
}
return Ok(trimmed.to_string());
}
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- Attach a TTY or provide input on stdin when the command prompts
- Handle this error at the call site and substitute your own fallback on EOF
- When scripting and you want the configured default, pipe a newline (blank line) instead of closing stdin
- For non-interactive flows, pass values via flags or config instead of interactive prompts
Example fix
// before
let value = prompt.interact_text()?; // Ctrl-D aborts the whole program
// after
let value = match prompt.interact_text() {
Ok(v) => v,
Err(e) if e.to_string().contains("No input received from stdin") => fallback.clone(),
Err(e) => return Err(e),
}; Defensive patterns
Strategy: try-catch
Validate before calling
use std::io::IsTerminal;
if !std::io::stdin().is_terminal() {
// non-interactive context: take values from flags/config instead of prompting
return Ok(config_value);
} Try / catch
let value = match prompt.interact_text() {
Ok(v) => v,
Err(ref e) if e.to_string().contains("No input received from stdin") => fallback.clone(),
Err(e) => return Err(e),
}; Prevention
- Pipe a newline rather than closing stdin when you want the configured default on empty input
- Provide flag/config equivalents for every interactive prompt so CI never blocks
- In test harnesses, keep stdin open or mock the IO pair instead of closing it
- Treat EOF as 'no answer', never as an error worth crashing on in interactive CLIs
When it happens
Trigger: User presses Ctrl-D at the prompt; a script pipes zero bytes then closes stdin (e.g. `true | zeroclaw ...`); stdin was already exhausted by an earlier prompt in the same process.
Common situations: CLI invoked non-interactively in CI/cron/piped contexts without input; here-docs consumed by a previous read; test harnesses that close stdin; dropped ssh sessions.
Related errors
- --description is required when stdin is not a TTY
- cli-secret-needs-tty
- webhook-audit: {e}
- unsupported room visibility '{other}': expected private or p
- channel '{}' does not support forge API requests
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8b3789952856f842.
Report an issue: GitHub.