{"record":{"id":"1d9d9a340d48c628","repo":"coding-horror/basic-computer-games","slug":"cannot-read-input-1d9d9a","errorCode":null,"errorMessage":"CANNOT READ INPUT!","messagePattern":"CANNOT READ INPUT!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"60_Mastermind/rust/Mastermind/src/main.rs","lineNumber":189,"sourceCode":"\n        //round loop\n        loop {\n            //loop condition\n            if num_moves > num_guesses {\n                println!(\"YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!\");\n                println!(\"THE ACTUAL COMBINATION WAS: {}\", answer._as_human_readible_chars());\n                human_score += num_moves;\n                print_scores(human_score,computer_score);\n                break;\n            }\n\n            //input loop\n            guess = GUESS::new(loop {\n                println!(\"\\nMOVE # {} GUESS: \", num_moves);\n\n                //get player move\n                let mut raw_input = String::new(); //temp variable to store user input\n                io::stdin().read_line(&mut raw_input).expect(\"CANNOT READ INPUT!\"); //read user input from standard input and store it to raw_input\n\n                //attempt to parse input\n                if raw_input.trim().eq_ignore_ascii_case(\"board\") {\n                    //print the board state\n                    print_board(&guesses);\n                    continue; //run loop again\n                }\n                else if raw_input.trim().eq_ignore_ascii_case(\"quit\") {\n                    //quit the game\n                    println!(\"QUITTER!  MY COMBINATION WAS: {}\\nGOOD BYE\", answer._as_human_readible_words());\n                    return; //exit the game\n                }\n                else {\n                    //parse input for a code\n                    match CODE::new_from_string(raw_input, num_colors) {\n                        Some(code) => {\n                            //ensure code is correct length\n                            if code.code.len() != num_positions { // if not","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/coding-horror/basic-computer-games/blob/5301155192d91d74d337899cecc59dbda59c4c17/60_Mastermind/rust/Mastermind/src/main.rs#L171-L207","documentation":"This panic is triggered by `.expect(\"CANNOT READ INPUT!\")` on `io::stdin().read_line()` inside the guess-input loop of Mastermind's human-codebreaker mode (line 189). The `.expect` converts any `read_line` I/O error — which the surrounding loop is designed to retry for *parse* errors — into a hard crash, defeating the purpose of the input loop.","triggerScenarios":"The player's stdin stream fails at the OS level (not a bad guess, which is handled by the `CODE::new_from_string` fallback). This occurs when stdin is closed mid-game, redirected from a file that ends before the game is over, or when running under a non-interactive runner. The `\"board\"` and `\"quit\"` string checks happen *after* `read_line` succeeds, so they cannot prevent this panic.","commonSituations":"Piping a scripted input file that has fewer lines than the game needs. Running the game in a CI pipeline or Docker container without an interactive TTY. A terminal emulator crash or SSH disconnect mid-session.","solutions":["Change the `.expect` to a `match` that on `Ok(0)` (EOF) prints a graceful exit message and `return`s or `break`s from the game loop.","On a non-fatal `Err`, print an error and `continue` the input loop rather than crashing.","Provide a `--no-interactive` mode or feed input from a file with enough lines to complete the game."],"exampleFix":"// before\nio::stdin().read_line(&mut raw_input).expect(\"CANNOT READ INPUT!\");\n\n// after\nmatch io::stdin().read_line(&mut raw_input) {\n    Ok(0) => { println!(\"\\nInput ended. Exiting game.\"); return; }\n    Ok(_) => {}\n    Err(e) => { eprintln!(\"Input error: {}\", e); continue; }\n}","handlingStrategy":"retry","validationCode":"// Ensure input stream has content before entering the game loop\nuse std::io::IsTerminal;\nif io::stdin().is_terminal() { /* safe to prompt interactively */ }","typeGuard":"fn try_read_line() -> Option<String> {\n    let mut buf = String::new();\n    (io::stdin().read_line(&mut buf).ok()? > 0).then_some(buf)\n}","tryCatchPattern":"match io::stdin().read_line(&mut raw_input) {\n    Ok(0) => { println!(\"Input ended.\"); return; }\n    Ok(_) => { /* parse input */ }\n    Err(e) => { eprintln!(\"{}\", e); continue; }\n}","preventionTips":["Use retry loops for parse errors but handle I/O errors separately with graceful exit.","Ensure piped input files provide enough lines for every prompt in every game phase.","Add a 'quit' command check as an escape hatch in long input loops."],"tags":["rust","stdin","io","panic","expect","cli-game","mastermind"],"backgroundTag":null,"analyzedSha":"5301155192d91d74d337899cecc59dbda59c4c17","analyzedAt":"2026-08-13T19:29:00.979Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}