{"record":{"id":"73960a46a04b3338","repo":"JuliusBrussee/caveman","slug":"write-stdout-w","errorCode":null,"errorMessage":"write stdout: %w","messagePattern":"write stdout: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/cmd/caveman-engine/main.go","lineNumber":133,"sourceCode":"}\n\n// emitCompressResult preserves the engine's byte-safety contract at the CLI\n// boundary. CCR failures are expected operational conditions: the engine has\n// already returned an accounted pass-through result, so stdout must still get\n// the original bytes. Any contradictory result fails rather than guessing.\nfunc emitCompressResult(input []byte, res engine.Result, compressErr error, stdout, stderr io.Writer) error {\n\tcode := \"\"\n\tif compressErr != nil {\n\t\tif !bytes.Equal(res.Output, input) || !res.PassedThrough() || res.TokensAfter != res.TokensBefore {\n\t\t\treturn fmt.Errorf(\"unsafe recovery failure result: %w\", compressErr)\n\t\t}\n\t\tcode = \"cave_ccr_unavailable\"\n\t\tif errors.Is(compressErr, ccr.ErrBudgetExceeded) {\n\t\t\tcode = \"cave_ccr_budget_exceeded\"\n\t\t}\n\t}\n\tif _, err := stdout.Write(res.Output); err != nil {\n\t\treturn fmt.Errorf(\"write stdout: %w\", err)\n\t}\n\t// Report stays on stderr so stdout remains clean payload bytes.\n\treport, err := json.Marshal(compressReport{Result: res, Error: code})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encode report: %w\", err)\n\t}\n\tif _, err := fmt.Fprintln(stderr, string(report)); err != nil {\n\t\treturn fmt.Errorf(\"write report: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc runDetect() {\n\tinput, err := readBoundedInput(os.Stdin, maxStdinBytes)\n\tif err != nil {\n\t\tfatal(\"read stdin: %v\", err)\n\t}\n\t// Detection needs no store.","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/engine/cmd/caveman-engine/main.go#L115-L151","documentation":"The engine CLI could not write the (pass-through) payload bytes to stdout. This is an environment-level pipe failure: the downstream reader closed the pipe, the terminal vanished, or a redirection target (file/disk) rejected the write. Because stdout is the clean payload channel, any write failure aborts emission.","triggerScenarios":"Running caveman-engine with stdout piped into a consumer that exits early (e.g. `caveman-engine compress | head -c 10`), stdout redirected to a full read-only filesystem, or running detached from a closed terminal.","commonSituations":"Shell pipelines where the next command exits before reading everything; disk full when redirecting to a file; SSH session dropped mid-run; CI capturing stdout into a bounded buffer.","solutions":["Check the wrapped errno: EPIPE means the consumer closed early; ENOSPC/EROFS mean the redirect target is full or read-only.","For pipelines, ensure the downstream consumer reads all of stdout or use a temp file instead of a pipe.","Free disk space or fix permissions on the redirection target.","Keep the report-on-stderr contract in mind: consume stderr separately, never merge it into stdout."],"exampleFix":"# before: consumer exits early, engine gets EPIPE\ncaveman-engine compress < in | head -c 10\n\n# after: buffer the full payload first\ncaveman-engine compress < in > out.tmp && head -c 10 out.tmp","handlingStrategy":"validation","validationCode":"// Ensure stdout is writable and has a consumer before heavy work\nif _, err := os.Stdout.Write(nil); err != nil {\n    return fmt.Errorf(\"stdout unavailable: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Buffer engine output to a temp file when piping into consumers that may exit early.","In pipelines, use set -o pipefail so EPIPE surfaces instead of being swallowed.","Verify the redirect target has space and write permission before starting the run."],"tags":["cli","stdout","epipe","io"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}