{"record":{"id":"c0963ba29773bd6e","repo":"Schniz/fnm","slug":"can-t-write-output","errorCode":null,"errorMessage":"Can't write output","messagePattern":"Can't write output","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/log_level.rs","lineNumber":54,"sourceCode":"        }\n    }\n\n    pub fn possible_values() -> &'static [&'static str; 4] {\n        &[\"quiet\", \"info\", \"all\", \"error\"]\n    }\n}\n\nimpl Display for LogLevel {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(self.as_str())\n    }\n}\n\n#[macro_export]\nmacro_rules! outln {\n    ($config:ident, $level:path, $($expr:expr),+) => {{\n        use $crate::log_level::LogLevel::*;\n        writeln!($config.log_level().writer_for($level), $($expr),+).expect(\"Can't write output\");\n    }}\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_is_writable() {\n        assert!(!LogLevel::Quiet.is_writable(LogLevel::Info));\n        assert!(!LogLevel::Error.is_writable(LogLevel::Info));\n        assert!(LogLevel::Info.is_writable(LogLevel::Info));\n        assert!(LogLevel::Info.is_writable(LogLevel::Error));\n    }\n}\n","sourceCodeStart":36,"sourceCodeEnd":70,"githubUrl":"https://github.com/Schniz/fnm/blob/86adc9676ceb2a509b21e75e74048b93c89f097d/src/log_level.rs#L36-L70","documentation":"The `outln!` macro writes one line to the writer selected by log level (stdout for info, stderr for error-level output, a sink for quiet mode) and unwraps the result. If the underlying write fails, the process panics with 'Can't write output'. The classic cause is EPIPE: Rust ignores SIGPIPE, so when a downstream reader like `head` closes the pipe, the next write returns a broken-pipe error instead of killing the process silently.","triggerScenarios":"Piping fnm output to a short-lived reader: `fnm env | head -1`, `fnm ls | grep -m1 pattern`, `fnm env --json | jq '.PATH'` where jq exits early; or redirecting to a device/file that fails writes (`fnm install 20 >/dev/full`, a read-only redirect).","commonSituations":"Shell scripts and dotfiles piping `fnm env` into `head`/`sed -n 1p`; editor plugins that open fnm, read a few lines, and close the pipe; log collectors closing stdin/stdout; writing to a full disk via redirection.","solutions":["Consume the full output instead of closing the reader early: capture to a file/variable (`fnm env > /tmp/env.sh`, `out=$(fnm env)`) and slice afterwards.","In wrappers, tolerate the exit: `fnm env | head -1 || true`.","Use quiet mode (`--quiet` / log-level quiet) when output is unwanted, routing writes to the sink so no failing write occurs.","If patching fnm: match on the write result and ignore `ErrorKind::BrokenPipe` (standard CLI behavior)."],"exampleFix":"// before (src/log_level.rs)\nwriteln!($config.log_level().writer_for($level), $($expr),+).expect(\"Can't write output\");\n\n// after\nlet _ = writeln!($config.log_level().writer_for($level), $($expr),+)\n    .inspect_err(|e| if e.kind() != std::io::ErrorKind::BrokenPipe { panic!(\"Can't write output: {e}\") });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"use std::io::Write;\nlet mut w = config.log_level().writer_for(LogLevel::Info);\nif let Err(e) = writeln!(w, \"{}\", line) {\n    if e.kind() == std::io::ErrorKind::BrokenPipe {\n        std::process::exit(0); // reader went away; exit quietly like other CLIs\n    }\n    panic!(\"Can't write output: {e}\");\n}","preventionTips":["Capture fnm output to a variable or file instead of piping into early-exiting readers.","Append `|| true` (or check exit codes) in scripts that pipe fnm output through head/grep -m1/jq.","Use `--log-level quiet` when output isn't needed, so writes go to a sink."],"tags":["rust","broken-pipe","stdout","cli","unix-signals","panic"],"backgroundTag":"broken-pipe","analyzedSha":"86adc9676ceb2a509b21e75e74048b93c89f097d","analyzedAt":"2026-08-16T21:43:05.725Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}