gleam-lang/gleam · error
Final result error writing
Error message
Final result error writing
What it means
In compiler-cli's main(), after Command::parse().run() returns Err, the error report is rendered to a buffered stderr and printed with expect("Final result error writing"). If that write fails (EPIPE/EIO on fd 2), the process panics — masking the real compilation error that was about to be printed. So you get an exit-by-panic plus a lost diagnostic whenever gleam fails AND stderr is unwritable at the same time.
Source
Thrown at compiler-cli/src/lib.rs:941
version: String,
},
}
pub fn main() {
initialise_logger();
panic::add_handler();
let stderr = cli::stderr_buffer_writer();
let result = get_current_directory()
.and_then(|working_directory| Command::parse().run(working_directory));
match result {
Ok(_) => {
tracing::info!("Successfully completed");
}
Err(error) => {
tracing::error!(error = ?error, "Failed");
let mut buffer = stderr.buffer();
error.pretty(&mut buffer);
stderr.print(&buffer).expect("Final result error writing");
std::process::exit(1);
}
}
}
fn command_check(paths: &ProjectPaths, target: Option<Target>) -> Result<()> {
let _ = build::main(
paths,
Options {
root_target_support: TargetSupport::Enforced,
warnings_as_errors: false,
codegen: Codegen::DepsOnly,
compile: Compile::All,
mode: Mode::Dev,
target,
no_print_progress: false,
},
build::download_dependencies(paths, cli::Reporter::new())?,View on GitHub (pinned to 7e623aa83d)
Solutions
- Redirect stderr to a file: `gleam build 2>err.log` — the error report always lands somewhere readable.
- Avoid `2>&1 | head` on failing commands; run the pipe over a complete capture: `gleam build >out.log 2>&1` then inspect the log.
- Re-run the command with stderr attached to a terminal to see the real Error::pretty diagnostic.
- Note the exit code: the panic aborts before std::process::exit(1), so the shell sees the panic exit path (101) instead of 1 — scripts should treat any nonzero code as failure.
Example fix
# before: real error is swallowed, second panic on stderr write gleam publish 2>&1 | head -n 1 # after: capture everything, then read it gleam publish >publish.log 2>&1; status=$?; head -n 50 publish.log; exit $status
Defensive patterns
Strategy: try-catch
Validate before calling
# ensure fd 2 is open and a valid destination before running gleam
gleam build 2>/dev/null # NO — this discards errors; prefer:
gleam build 2>err.log || { cat err.log >&2; exit 1; } Try / catch
// Wrap the CLI as a subprocess: a panic (including this expect) becomes a
// non-zero exit status you can observe instead of crashing your host.
let status = std::process::Command::new("gleam").args(args).stderr(std::process::Stdio::piped()).output()?;
if !status.status.success() {
eprintln!("gleam failed:\n{}", String::from_utf8_lossy(&status.stderr));
}
// Embedding main() directly? use std::panic::catch_unwind(AssertUnwindSafe(|| main())) Prevention
- In CI, capture gleam output to files rather than piping through head/grep -q.
- Keep fd 2 attached (never `2>&-`) when invoking gleam from wrappers.
- Treat any nonzero exit (including panic exit 101) as failure and print the captured log — don't rely on gleam's own stderr rendering.
When it happens
Trigger: A failing gleam command with stderr piped to a reader that already exited: `gleam build 2>&1 | head -n1` where the build errors after head quit; fd 2 closed with `2>&-`; stderr redirected to a dead pseudo-terminal or socket (detached daemon, killed CI step).
Common situations: CI scripts that pipe all output through head/grep -q and then check exit codes; wrapper tools that close both pipes on first error; nohup/disowned processes whose terminal vanished.
Related errors
- Writing warning to stderr
- `panic` expression evaluated.
- Unable to start Tokio async runtime
- joining_lsp_threads
- invalid folder
AI-assisted analysis of gleam-lang/gleam@7e623aa83d (2026-08-17).
Data as JSON: /api/errors/35aaec8a4f9e58ae.
Report an issue: GitHub.