JuliusBrussee/caveman · error

write stdout: %w

Error message

write stdout: %w

What it means

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.

Source

Thrown at engine/cmd/caveman-engine/main.go:133

}

// emitCompressResult preserves the engine's byte-safety contract at the CLI
// boundary. CCR failures are expected operational conditions: the engine has
// already returned an accounted pass-through result, so stdout must still get
// the original bytes. Any contradictory result fails rather than guessing.
func emitCompressResult(input []byte, res engine.Result, compressErr error, stdout, stderr io.Writer) error {
	code := ""
	if compressErr != nil {
		if !bytes.Equal(res.Output, input) || !res.PassedThrough() || res.TokensAfter != res.TokensBefore {
			return fmt.Errorf("unsafe recovery failure result: %w", compressErr)
		}
		code = "cave_ccr_unavailable"
		if errors.Is(compressErr, ccr.ErrBudgetExceeded) {
			code = "cave_ccr_budget_exceeded"
		}
	}
	if _, err := stdout.Write(res.Output); err != nil {
		return fmt.Errorf("write stdout: %w", err)
	}
	// Report stays on stderr so stdout remains clean payload bytes.
	report, err := json.Marshal(compressReport{Result: res, Error: code})
	if err != nil {
		return fmt.Errorf("encode report: %w", err)
	}
	if _, err := fmt.Fprintln(stderr, string(report)); err != nil {
		return fmt.Errorf("write report: %w", err)
	}
	return nil
}

func runDetect() {
	input, err := readBoundedInput(os.Stdin, maxStdinBytes)
	if err != nil {
		fatal("read stdin: %v", err)
	}
	// Detection needs no store.

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check the wrapped errno: EPIPE means the consumer closed early; ENOSPC/EROFS mean the redirect target is full or read-only.
  2. For pipelines, ensure the downstream consumer reads all of stdout or use a temp file instead of a pipe.
  3. Free disk space or fix permissions on the redirection target.
  4. Keep the report-on-stderr contract in mind: consume stderr separately, never merge it into stdout.

Example fix

# before: consumer exits early, engine gets EPIPE
caveman-engine compress < in | head -c 10

# after: buffer the full payload first
caveman-engine compress < in > out.tmp && head -c 10 out.tmp
Defensive patterns

Strategy: validation

Validate before calling

// Ensure stdout is writable and has a consumer before heavy work
if _, err := os.Stdout.Write(nil); err != nil {
    return fmt.Errorf("stdout unavailable: %w", err)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/73960a46a04b3338. Report an issue: GitHub.