gleam-lang/gleam · error
Writing warning to stderr
Error message
Writing warning to stderr
What it means
ConsoleWarningEmitter::emit_warning pretty-prints every compiler warning to stderr through a termcolor BufferWriter and calls expect("Writing warning to stderr") on the print result. print returns io::Result, and it fails when the underlying write(2) to fd 2 fails — almost always EPIPE or EIO because the process reading gleam's stderr has gone away. The panic happens while printing a warning, so it can abort a build that otherwise succeeded.
Source
Thrown at compiler-cli/src/fs.rs:892
action: FileIoAction::Canonicalise,
kind: FileKind::File,
path: Utf8PathBuf::from(path),
err: Some(err.to_string()),
})
.map(|pb| Utf8PathBuf::from_path_buf(pb).expect("Non Utf8 Path"))
}
#[derive(Debug, Clone, Copy)]
pub struct ConsoleWarningEmitter;
impl WarningEmitterIO for ConsoleWarningEmitter {
fn emit_warning(&self, warning: Warning) {
let buffer_writer = crate::cli::stderr_buffer_writer();
let mut buffer = buffer_writer.buffer();
warning.pretty(&mut buffer);
buffer_writer
.print(&buffer)
.expect("Writing warning to stderr");
}
}
/// Returns root of Git repository in base path if it is initialised.
pub fn get_git_repository_root(mut path: Utf8PathBuf) -> Option<Utf8PathBuf> {
loop {
if path.join(".git").is_dir() {
return Some(path);
}
path = {
let path = path.parent()?;
path.into()
}
}
}
#[derive(Debug)]View on GitHub (pinned to 7e623aa83d)
Solutions
- Redirect stderr to a file instead of a pipe that can close early: `gleam build 2>warnings.txt`.
- If you must pipe, keep the reader alive for the whole run (use `| cat` or `| less -X` rather than `| head`).
- Reduce warning volume first (fix warnings, or pass --warnings-as-errors in CI) so fewer writes happen.
- If embedding the CLI, swap ConsoleWarningEmitter for a WarningEmitterIO implementation that maps io::Error to a graceful skip instead of expect.
Example fix
# before: reader exits after 5 lines, later warning writes get EPIPE gleam build 2>&1 | head -n 5 # after: stderr captured to a file, no broken pipe possible gleam build 2>warnings.txt; head -n 5 warnings.txt
Defensive patterns
Strategy: fallback
Validate before calling
# CLI users: verify fd 2 is writable before a long build if ! (echo -n '' >&2) 2>/dev/null; then exec 2>gleam-stderr.log; fi gleam build
Try / catch
// Embedders: implement a warning emitter that degrades gracefully
struct TolerantWarningEmitter;
impl WarningEmitterIO for TolerantWarningEmitter {
fn emit_warning(&self, warning: Warning) {
let w = cli::stderr_buffer_writer();
let mut buf = w.buffer();
warning.pretty(&mut buf);
if w.print(&buf).is_err() {
// stderr is gone (EPIPE); drop the warning instead of panicking
let _ = std::fs::write("gleam-warnings.log", format!("{warning:?}"));
}
}
} Prevention
- Redirect stderr to a file (`gleam build 2>warn.log`) in scripts instead of piping into short-lived readers like head.
- Never close the read end of a pipe attached to gleam's stderr before the process exits.
- Fix warnings or run with --warnings-as-errors in CI so late warning writes can't kill the build.
When it happens
Trigger: Running any gleam command with stderr piped to a consumer that exits early: `gleam build 2>&1 | head -n 5`, `gleam check 2> >(head -1)` with process substitution, or CI wrappers that close the pipe. Also when fd 2 is closed (2>&-) or an SSH/terminal session dies mid-build while many warnings are being emitted.
Common situations: Piping compiler output into head/less/grep -m1 in scripts, editor task runners that kill the pipe after first output, detached processes whose stderr was a socket that closed, and sandboxed CI that revokes fd 2.
Related errors
- Final result error writing
- `panic` expression evaluated.
- joining_lsp_threads
- invalid folder
- Unable to start Tokio async runtime
AI-assisted analysis of gleam-lang/gleam@7e623aa83d (2026-08-17).
Data as JSON: /api/errors/441bb694fe72ec8d.
Report an issue: GitHub.