{"record":{"id":"93edc28ec7fe7f6a","repo":"facebook/flow","slug":"failed-to-write-json-errors","errorCode":null,"errorMessage":"failed to write json errors","messagePattern":"failed to write json errors","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_cli/src/check_contents_command.rs","lineNumber":145,"sourceCode":"         suppressed_errors: &[(PrintableError<Loc>, BTreeSet<Loc>)]| {\n            let strip_root = strip_root\n                .as_deref()\n                .map(|root| root.to_string_lossy().into_owned());\n            let stdout = std::io::stdout();\n            let mut out = stdout.lock();\n            flow_common_errors::error_utils::json_output::print_errors_with_offset_kind(\n                &mut out,\n                strip_root.as_deref(),\n                suppressed_errors,\n                pretty,\n                json_version\n                    .unwrap_or(flow_common_errors::error_utils::json_output::JsonVersion::JsonV1),\n                &stdin_file,\n                offset_kind,\n                errors,\n                warnings,\n            )\n            .expect(\"failed to write json errors\");\n            out.flush().expect(\"failed to flush json errors\");\n        };\n    match response {\n        server_prot::response::StatusResponse::ERRORS {\n            errors,\n            warnings,\n            suppressed_errors,\n        } => {\n            if json {\n                print_json(&errors, &warnings, &suppressed_errors)\n            } else {\n                let stdout = std::io::stdout();\n                let mut out = stdout.lock();\n                flow_common_errors::error_utils::cli_output::print_errors(\n                    &mut out,\n                    &error_flags,\n                    &stdin_file,\n                    strip_root.as_deref(),","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_cli/src/check_contents_command.rs#L127-L163","documentation":"In flow's check-contents command, once the flow server replies with a StatusResponse::ERRORS payload and JSON output is active, print_errors_with_offset_kind serializes errors, warnings, and suppressed errors to locked stdout and returns io::Result. The .expect() converts any write failure into a panic, aborting before the proper exit code can be reported. The dominant cause is EPIPE: Rust ignores SIGPIPE, so a downstream pipe reader (head, grep -m1, grep -q) that already exited surfaces as a write error here instead of killing the process silently.","triggerScenarios":"`flow check-contents file.js --json | head -1` while many diagnostics are still being written; piping into `grep -q pattern` which exits on first match; redirecting --json output to a full filesystem or /dev/full (ENOSPC); invoking the CLI with stdout closed (`>&-`).","commonSituations":"CI scripts and shell pipelines that stream JSON error reports into filters which terminate early; docker/cron contexts where stdout points to /dev/full or a closed fd; pagers or log collectors that stop reading partway through a large report.","solutions":["Buffer the pipeline so the CLI can finish writing: `flow check-contents file.js --json | cat | head -1`, or drop the early-exiting filter","Write the report to a file and post-process it: `flow check-contents file.js --json > report.json`","If redirecting to a file or device, free disk space or fix the target (df -h; verify the redirect path)","Maintainer: match on the Result and exit quietly with code 141 on ErrorKind::BrokenPipe instead of expect"],"exampleFix":"// before\nprint_errors_with_offset_kind(&mut out, /* ... */)\n    .expect(\"failed to write json errors\");\n\n// after\nif let Err(e) = print_errors_with_offset_kind(&mut out, /* ... */) {\n    if e.kind() == std::io::ErrorKind::BrokenPipe {\n        std::process::exit(141);\n    }\n    panic!(\"failed to write json errors: {e}\");\n}","handlingStrategy":"try-catch","validationCode":"// If you spawn this CLI, never close its stdout pipe early —\n// drain it fully, or kill the child first:\nlet mut child = std::process::Command::new(\"flow\")\n    .args([\"check-contents\", \"--json\", \"file.js\"])\n    .stdout(std::process::Stdio::piped())\n    .spawn()?;\nlet mut out = child.stdout.take().unwrap();\nlet mut buf = Vec::new();\nout.read_to_end(&mut buf)?; // consume everything before exiting\nchild.wait()?;","typeGuard":null,"tryCatchPattern":"match print_errors_with_offset_kind(&mut out, /* ... */) {\n    Ok(()) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe =>\n        std::process::exit(141), // reader gone: exit quietly like C tools\n    Err(e) => panic!(\"failed to write json errors: {e}\"),\n}","preventionTips":["Never pipe this command directly into an early-exiting reader (head -n, grep -m1, grep -q); insert `| cat` or write to a file","In CI, redirect JSON reports to files and post-process the files","Add a `| head -1` smoke test to pipelines so broken-pipe panics are caught before shipping"],"tags":["rust","flow-cli","stdout","broken-pipe","json","panic"],"backgroundTag":"broken-pipe","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-23T11:17:13.642Z"}