JuliusBrussee/caveman · error
encode report
Error message
encode report: %w
What it means
After writing payload bytes, emitCompressResult marshals the compressReport (Result + error code) to JSON for stderr. If json.Marshal fails, the error is wrapped as "encode report: %w". This is nearly impossible with plain data and signals an internal encoding problem.
Solutions
- Check recently added fields on compressReport or engine.Result for non-JSON-serializable types.
- Add or fix MarshalJSON on the offending type, or drop/redact the unserializable field.
- Marshal the report in a unit test to catch regressions before runtime.
Example fix
// before
type compressReport struct {
Notify func() `json:"notify"` // unsupported
}
// after
type compressReport struct {
Result engine.Result
Error string
} Defensive patterns
Strategy: try-catch
Validate before calling
// smoke-test report serialization in CI
if _, err := json.Marshal(compressReport{Result: engine.Result{}, Error: "cave_ccr_unavailable"}); err != nil { t.Fatal(err) } Try / catch
if _, err := json.Marshal(report); err != nil {
return fmt.Errorf("encode report: %w", err) // surface, then inspect %v of wrapped cause
} Prevention
- Keep compressReport fields JSON-serializable (no chan/func/cycles)
- Add a marshal round-trip test when extending Result
- Prefer string/number fields in report structs
When it happens
Trigger: json.Marshal(compressReport{Result: res, Error: code}) returns an error — e.g. a field in Result/engine.Result cannot be serialized (unsupported type such as a channel or func slipped into the struct).
Common situations: A developer adds a field of an unsupported type (chan, func, or cyclic structure) to compressReport or the embedded engine.Result; custom MarshalJSON panics/returns error on unusual token counts.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
- assembly slot content is not JSON-serializable
- assembly slot content is not JSON-serializable
- cave_sandbox_failure_not_serializable
- caveman agent: value is not canonically serializable
- encode before evidence
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/685b57a9df198e21.
Report an issue: GitHub.
Appendix: source
Thrown at engine/cmd/caveman-engine/main.go:138
// 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.
eng := engine.New(nil, nil)
fmt.Println(eng.Detect(input))
}
func runRetrieve(args []string) {View on GitHub (pinned to 3ee70a1026)