coding-horror/basic-computer-games · error
Failed to read line.
Error message
Failed to read line.
What it means
This panic is triggered by `.expect("Failed to read line.")` on `io::stdin().read_line()` inside the shared `prompt()` utility in Mugwump's `util.rs` (line 10). Because this is a reusable prompt function called by all game input sites, a stdin failure here crashes the program at *any* prompt, not just one specific interaction.
Source
Thrown at 62_Mugwump/rust/src/util.rs:10
use std::io;
pub fn prompt(msg: &str) -> String {
println!("\n{}", msg);
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Failed to read line.");
input.trim().to_string()
}
pub fn prompt_bool(msg: &str) -> Option<bool> {
loop {
let response = prompt(msg);
match response.to_uppercase().as_str() {
"Y" | "YES" => return Some(true),
"N" | "NO" => return Some(false),
_ => println!("PLEASE ENTER (Y)ES or (N)O."),
}
}
}
View on GitHub (pinned to 5301155192)
Solutions
- Change `prompt()` to return `Option<String>` or `Result<String, io::Error>`, returning `None`/`Err` on read failure instead of panicking.
- Detect EOF (0 bytes) and signal the caller to exit the game cleanly.
- When running non-interactively, provide an input stream that contains a line for every prompt the game will issue.
Example fix
// before
pub fn prompt(msg: &str) -> String {
println!("\n{}", msg);
let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line.");
input.trim().to_string()
}
// after
pub fn prompt(msg: &str) -> Option<String> {
println!("\n{}", msg);
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(0) => None,
Ok(_) => Some(input.trim().to_string()),
Err(_) => None,
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate that stdin is readable before calling prompt()
use std::io::IsTerminal;
if !io::stdin().is_terminal() && std::env::var("INTERACTIVE_TEST").is_err() {
eprintln!("Warning: no interactive stdin detected.");
} Type guard
pub fn prompt(msg: &str) -> Option<String> {
println!("\n{}", msg);
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(0) | Err(_) => None,
Ok(_) => Some(input.trim().to_string()),
}
} Try / catch
match io::stdin().read_line(&mut input) {
Ok(0) => { println!("\nGoodbye!"); std::process::exit(0); }
Ok(_) => { /* proceed */ }
Err(_) => { println!("Input error. Try again."); }
} Prevention
- Design shared input utilities to return Option/Result, not String, so callers can handle failures.
- Centralize stdin error handling in one safe function rather than per-call .expect().
- Test all interactive paths with both piped and TTY stdin.
When it happens
Trigger: `read_line` returns `Err` or EOF. Since `prompt()` returns `String` (not `Result`), there is no way for callers to handle a read failure — the `.expect` is the only failure path. The function is used for all text input in the game.
Common situations: Non-interactive execution, piped input that runs out, terminal disconnection. Because every input goes through this function, any of dozens of game prompts can trigger it.
Related errors
- Failed to read line.
- **Failed to read input**
- Failed to read line.
- Failed to read the line
- No valid input
AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13).
Data as JSON: /api/errors/f4194174223e9922.
Report an issue: GitHub.