{"record":{"id":"79badc702802ea42","repo":"Schniz/fnm","slug":"can-t-read-user-input","errorCode":null,"errorMessage":"Can't read user input","messagePattern":"Can't read user input","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/commands/use.rs","lineNumber":194,"sourceCode":"fn should_install_interactively(requested_version: &UserVersion) -> bool {\n    use std::io::{IsTerminal, Write};\n\n    if !(std::io::stdout().is_terminal() && std::io::stdin().is_terminal()) {\n        return false;\n    }\n\n    let error_message = format!(\n        \"fnm can't find an installed Node version matching {}.\",\n        requested_version.to_string().italic()\n    );\n    eprintln!(\"{}\", error_message.red());\n    let do_you_want = format!(\"Do you want to install it? {} [y/N]:\", \"answer\".bold());\n    eprint!(\"{} \", do_you_want.yellow());\n    std::io::stdout().flush().unwrap();\n    let mut s = String::new();\n    std::io::stdin()\n        .read_line(&mut s)\n        .expect(\"Can't read user input\");\n\n    s.trim().to_lowercase() == \"y\"\n}\n\nfn warn_if_multishell_path_not_in_path_env_var(\n    multishell_path: &std::path::Path,\n    config: &FnmConfig,\n) {\n    if let Some(path_var) = std::env::var_os(\"PATH\") {\n        let bin_path = if cfg!(unix) {\n            multishell_path.join(\"bin\")\n        } else {\n            multishell_path.to_path_buf()\n        };\n\n        let fixed_path = bin_path.to_str().and_then(shell::maybe_fix_windows_path);\n        let fixed_path = fixed_path.as_deref();\n","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/Schniz/fnm/blob/86adc9676ceb2a509b21e75e74048b93c89f097d/src/commands/use.rs#L176-L212","documentation":"When `fnm use <version>` cannot find the requested installed version, it prints a colored install prompt and reads one line from stdin. If `read_line` returns an io error — stdin closed (EBADF), redirected to something unreadable, or an I/O failure — `.expect(\"Can't read user input\")` panics. Plain EOF (e.g. stdin from /dev/null) is not an error: it leaves the answer empty, which counts as 'no'.","triggerScenarios":"Running `fnm use <missing-version>` in a context where stdin is closed or not open for reading — `fnm use 18 0<&-`, stdin redirected to a directory, or fnm invoked from a daemon/scheduled task/CI job with no console attached.","commonSituations":"CI pipelines and cron jobs calling `fnm use` without pre-installing the version; scripts run with detached stdin; IDE external tasks with no terminal; `fnm use` inside hooks (pre-commit, direnv) where stdin is consumed or closed.","solutions":["Pre-install so the prompt never appears: `fnm install 18 && fnm use 18`.","Give stdin a safe fallback that declines gracefully: `fnm use 18 </dev/null` (EOF → answer 'no').","In non-interactive contexts, prefer `fnm use --version-file` flows or set FNM_VERSION_FILE_STRATEGY and ensure versions are pre-installed.","If patching fnm: treat a read error as 'no' instead of panicking — `.unwrap_or_default()` on the read result."],"exampleFix":"// before (src/commands/use.rs)\nstd::io::stdin().read_line(&mut s).expect(\"Can't read user input\");\n\n// after\nlet _ = std::io::stdin().read_line(&mut s); // read failure => empty answer => \"no\"","handlingStrategy":"validation","validationCode":"# only attempt interactive fnm use when stdin is readable\nif [ -t 0 ] || [ -r /dev/stdin ]; then\n  fnm use \"$VERSION\" </dev/tty 2>/dev/null || fnm use \"$VERSION\"\nelse\n  fnm install \"$VERSION\" && fnm use \"$VERSION\"  # non-interactive path, no prompt\nfi","typeGuard":null,"tryCatchPattern":"let mut s = String::new();\nmatch std::io::stdin().read_line(&mut s) {\n    Ok(_) => s.trim().eq_ignore_ascii_case(\"y\"),\n    Err(_) => false, // unreadable stdin => treat as \"no\", never panic\n}","preventionTips":["Pre-install required versions in CI/cron so the y/N prompt never triggers.","Redirect stdin from /dev/null in non-interactive scripts (`fnm use v18 </dev/null` declines cleanly).","Attach a TTY (`</dev/tty`) when you genuinely want the prompt inside wrappers."],"tags":["rust","stdin","interactive-prompt","use-command","cli","panic"],"backgroundTag":"stdin-read-failed","analyzedSha":"86adc9676ceb2a509b21e75e74048b93c89f097d","analyzedAt":"2026-08-16T21:43:05.725Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}