coding-horror/basic-computer-games · error

**Failed to read input**

Error message

**Failed to read input**

What it means

This panic is triggered by `.expect("**Failed to read input**")` on `io::stdin().read_line()` inside the `prompt()` function in Queen's `util.rs` (line 19). This function is the universal input handler, returning a `PromptResult` enum (`Numeric`, `YesNo`, etc.) for both numeric and text input. The `.expect` converts any I/O-level stdin failure into a crash before the enum-dispatch logic runs.

Source

Thrown at 72_Queen/rust/src/util.rs:19

use std::io;

pub enum PromptResult {
    Normal(String),
    YesNo(bool),
    Numeric(i32),
}

pub fn prompt(is_numeric: Option<bool>, msg: &str) -> PromptResult {
    use PromptResult::*;

    println!("{msg}");

    loop {
        let mut input = String::new();

        io::stdin()
            .read_line(&mut input)
            .expect("**Failed to read input**");

        if let Some(is_numeric) = is_numeric {
            let input = input.trim();

            if is_numeric {
                if let Ok(n) = input.parse::<i32>() {
                    return Numeric(n);
                }
                println!("PLEASE ENTER A VALID NUMBER!");
            } else {
                match input.to_uppercase().as_str() {
                    "YES" | "Y" => return YesNo(true),
                    "NO" | "N" => return YesNo(false),
                    _ => println!("PLEASE ENTER (Y)ES OR (N)O."),
                }
            }
        } else {
            return Normal(input);

View on GitHub (pinned to 5301155192)

Solutions

  1. Add a `Cancelled` or `Failed` variant to `PromptResult` and return it on `Ok(0)` or `Err` instead of panicking.
  2. Change the function to return `Option<PromptResult>` so callers can detect EOF and exit.
  3. For non-interactive use, provide complete input covering every `prompt()` call the game makes.

Example fix

// before
io::stdin().read_line(&mut input).expect("**Failed to read input**");

// after
let bytes = io::stdin().read_line(&mut input);
match bytes {
    Ok(0) | Err(_) => {
        println!("\nInput closed.");
        std::process::exit(0);
    }
    Ok(_) => {}
}
Defensive patterns

Strategy: try-catch

Validate before calling

use std::io::IsTerminal;
if !io::stdin().is_terminal() {
    eprintln!("Warning: non-interactive stdin.");
}

Type guard

pub fn prompt(is_numeric: Option<bool>, msg: &str) -> Option<PromptResult> {
    // ...
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(0) | Err(_) => return None,
        Ok(_) => { /* parse and return PromptResult */ }
    }
}

Try / catch

match io::stdin().read_line(&mut input) {
    Ok(0) => { println!("\nInput closed."); std::process::exit(0); }
    Ok(_) => { /* dispatch to Numeric/YesNo */ }
    Err(e) => { eprintln!("{}", e); }
}

Prevention

When it happens

Trigger: `read_line` returns `Err` or EOF. The function's internal validation (`input.parse::<i32>()`, uppercase matching for YES/NO) happens *after* `read_line` succeeds, so invalid *content* never reaches the `.expect`. Only stream-level failures trigger it.

Common situations: Non-interactive execution. Piped input runs out mid-game. Terminal disconnection. The game is launched from a context (GUI app, IDE run config) without a wired-up stdin.

Related errors


AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13). Data as JSON: /api/errors/2249f7acf13b50f4. Report an issue: GitHub.