coding-horror/basic-computer-games · error

No valid input

Error message

No valid input

What it means

This panic is triggered by `.expect("No valid input")` on `io::stdin().read_line()` in the Love program. The message is misleading — the failure is not about input *validity* (that is handled downstream by the `input.trim()` logic) but about `read_line` returning `Err` due to an I/O-level failure reading from stdin.

Source

Thrown at 58_Love/rust/src/main.rs:63

        vec![6, 6, 9, 3, 12, 6, 7, 1, 10],
        vec![7, 6, 7, 3, 13, 6, 6, 2, 10],
        vec![7, 6, 7, 3, 13, 14, 10],
        vec![8, 6, 5, 3, 14, 6, 6, 2, 10],
        vec![8, 6, 5, 3, 14, 6, 7, 1, 10],
        vec![9, 6, 3, 3, 15, 6, 16, 1, 1],
        vec![9, 6, 3, 3, 15, 6, 15, 2, 1],
        vec![10, 6, 1, 3, 16, 6, 14, 3, 1],
        vec![10, 10, 16, 6, 12, 5, 1],
        vec![11, 8, 13, 27, 1],
        vec![11, 8, 13, 27, 1],
        vec![60],
    ];

    const ROW_LEN: usize = 60;
    show_intro();

    let mut input: String = String::new();
    io::stdin().read_line(&mut input).expect("No valid input");
    let input = if input.len() == 1 {
        "LOVE"
    } else {
        input.trim()
    };
    // repeat the answer to fill the whole line, we will show chunks of this when needed
    let input = input.repeat(ROW_LEN / (input.len()) + 1);

    // Now lets display the Love
    print!("{}", "\n".repeat(9));
    for row in data {
        let mut print_or_pass = PrintOrPass::Print;
        let mut current_start = 0;
        for count in row {
            match print_or_pass {
                PrintOrPass::Print => {
                    print!("{}", &input[current_start..count + current_start]);
                    print_or_pass = PrintOrPass::Pass;

View on GitHub (pinned to 5301155192)

Solutions

  1. Replace `.expect()` with a `match` that distinguishes `Ok(0)` (EOF) from genuine `Err`, exiting gracefully on EOF.
  2. Add a pre-check or configuration option to read the word from a command-line argument or file when stdin is not interactive.
  3. Wrap the entire read in a retry loop with a maximum attempt count for transient I/O errors.

Example fix

// before
io::stdin().read_line(&mut input).expect("No valid input");

// after
let bytes_read = io::stdin().read_line(&mut input);
match bytes_read {
    Ok(0) => { println!("No input received. Exiting."); return; }
    Ok(_) => {}
    Err(e) => { eprintln!("Failed to read input: {}", e); return; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

use std::io::IsTerminal;
if !io::stdin().is_terminal() {
    // Provide input via argument or file instead
}

Type guard

fn safe_read_line() -> Option<String> {
    let mut buf = String::new();
    match io::stdin().read_line(&mut buf) {
        Ok(0) | Err(_) => None,
        Ok(_) => Some(buf.trim().to_string()),
    }
}

Try / catch

let mut input = String::new();
match io::stdin().read_line(&mut input) {
    Ok(0) => { println!("No input. Exiting."); return; }
    Ok(_) => { /* proceed with input.trim() */ }
    Err(e) => { eprintln!("{}", e); return; }
}

Prevention

When it happens

Trigger: Stdin is unavailable, closed, or returns an I/O error before any line is read. This happens before the program reaches the `input.repeat(ROW_LEN / input.len() + 1)` line, so the panic preempts the separate divide-by-zero risk on empty input. Common when the process inherits no stdin handle.

Common situations: Running inside Docker/CI without `-it` flags. Launching from a desktop launcher or IDE run configuration that does not allocate a terminal. Redirecting stdin from a broken pipe or a file that triggers an I/O error.

Related errors


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