{"record":{"id":"35aaec8a4f9e58ae","repo":"gleam-lang/gleam","slug":"final-result-error-writing","errorCode":null,"errorMessage":"Final result error writing","messagePattern":"Final result error writing","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-cli/src/lib.rs","lineNumber":941,"sourceCode":"        version: String,\n    },\n}\n\npub fn main() {\n    initialise_logger();\n    panic::add_handler();\n    let stderr = cli::stderr_buffer_writer();\n    let result = get_current_directory()\n        .and_then(|working_directory| Command::parse().run(working_directory));\n    match result {\n        Ok(_) => {\n            tracing::info!(\"Successfully completed\");\n        }\n        Err(error) => {\n            tracing::error!(error = ?error, \"Failed\");\n            let mut buffer = stderr.buffer();\n            error.pretty(&mut buffer);\n            stderr.print(&buffer).expect(\"Final result error writing\");\n            std::process::exit(1);\n        }\n    }\n}\n\nfn command_check(paths: &ProjectPaths, target: Option<Target>) -> Result<()> {\n    let _ = build::main(\n        paths,\n        Options {\n            root_target_support: TargetSupport::Enforced,\n            warnings_as_errors: false,\n            codegen: Codegen::DepsOnly,\n            compile: Compile::All,\n            mode: Mode::Dev,\n            target,\n            no_print_progress: false,\n        },\n        build::download_dependencies(paths, cli::Reporter::new())?,","sourceCodeStart":923,"sourceCodeEnd":959,"githubUrl":"https://github.com/gleam-lang/gleam/blob/7e623aa83da3776faee50ca4ab9a6c40124acd95/compiler-cli/src/lib.rs#L923-L959","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before: real error is swallowed, second panic on stderr write\ngleam publish 2>&1 | head -n 1\n\n# after: capture everything, then read it\ngleam publish >publish.log 2>&1; status=$?; head -n 50 publish.log; exit $status","handlingStrategy":"try-catch","validationCode":"# ensure fd 2 is open and a valid destination before running gleam\ngleam build 2>/dev/null  # NO — this discards errors; prefer:\ngleam build 2>err.log || { cat err.log >&2; exit 1; }","typeGuard":null,"tryCatchPattern":"// Wrap the CLI as a subprocess: a panic (including this expect) becomes a\n// non-zero exit status you can observe instead of crashing your host.\nlet status = std::process::Command::new(\"gleam\").args(args).stderr(std::process::Stdio::piped()).output()?;\nif !status.status.success() {\n    eprintln!(\"gleam failed:\\n{}\", String::from_utf8_lossy(&status.stderr));\n}\n// Embedding main() directly? use std::panic::catch_unwind(AssertUnwindSafe(|| main()))","preventionTips":["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."],"tags":["panic","stderr","pipe","epipe","io","exit-code","cli"],"backgroundTag":"broken-pipe","analyzedSha":"7e623aa83da3776faee50ca4ab9a6c40124acd95","analyzedAt":"2026-08-17T00:07:02.091Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}