Schniz/fnm · error
Can't write output
Error message
Can't write output
What it means
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.
Source
Thrown at src/log_level.rs:54
}
}
pub fn possible_values() -> &'static [&'static str; 4] {
&["quiet", "info", "all", "error"]
}
}
impl Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[macro_export]
macro_rules! outln {
($config:ident, $level:path, $($expr:expr),+) => {{
use $crate::log_level::LogLevel::*;
writeln!($config.log_level().writer_for($level), $($expr),+).expect("Can't write output");
}}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_writable() {
assert!(!LogLevel::Quiet.is_writable(LogLevel::Info));
assert!(!LogLevel::Error.is_writable(LogLevel::Info));
assert!(LogLevel::Info.is_writable(LogLevel::Info));
assert!(LogLevel::Info.is_writable(LogLevel::Error));
}
}
View on GitHub (pinned to 86adc9676c)
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).
Example fix
// before (src/log_level.rs)
writeln!($config.log_level().writer_for($level), $($expr),+).expect("Can't write output");
// after
let _ = writeln!($config.log_level().writer_for($level), $($expr),+)
.inspect_err(|e| if e.kind() != std::io::ErrorKind::BrokenPipe { panic!("Can't write output: {e}") }); Defensive patterns
Strategy: try-catch
Try / catch
use std::io::Write;
let mut w = config.log_level().writer_for(LogLevel::Info);
if let Err(e) = writeln!(w, "{}", line) {
if e.kind() == std::io::ErrorKind::BrokenPipe {
std::process::exit(0); // reader went away; exit quietly like other CLIs
}
panic!("Can't write output: {e}");
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Failed to grab exit code
- Can't read user input
- Can't get a temporary file
- Invalid OS string
- choosing base strategy
AI-assisted analysis of Schniz/fnm@86adc9676c (2026-08-16).
Data as JSON: /api/errors/c0963ba29773bd6e.
Report an issue: GitHub.