JuliusBrussee/caveman · error
cachebench: observation request %q body digest mismatch
Error message
cachebench: observation request %q body digest mismatch
What it means
The final join check: an observation's RequestBodySHA256 must be non-empty and equal to the trace record's BodySHA256. A mismatch means the body the provider actually saw differs from the body the trace recorded, invalidating any cache-reuse attribution.
Source
Thrown at cacheengine/cachebench/observed.go:65
}
if strings.TrimSpace(request.Provider) == "" || strings.TrimSpace(request.Epoch) == "" || !validUniqueJSONObject(request.Body) || request.BodySHA256 == "" || request.BodySHA256 != bodyDigest(request.Body) {
return Report{}, fmt.Errorf("cachebench: trace request %q has invalid identity, body, or digest", request.RequestID)
}
if request.StableSegmentCount < 0 || request.StableSegmentCount > len(request.Prefix) || !validPrefix(request.Prefix) {
return Report{}, fmt.Errorf("cachebench: trace request %q has invalid prefix", request.RequestID)
}
byID[request.RequestID] = request
}
for _, record := range records {
request, exists := byID[record.RequestID]
if !exists {
return Report{}, fmt.Errorf("cachebench: observation request %q absent from trace", record.RequestID)
}
if record.Provider != request.Provider || record.Epoch != request.Epoch {
return Report{}, fmt.Errorf("cachebench: observation request %q provider/epoch mismatch", record.RequestID)
}
if record.RequestBodySHA256 == "" || record.RequestBodySHA256 != request.BodySHA256 {
return Report{}, fmt.Errorf("cachebench: observation request %q body digest mismatch", record.RequestID)
}
}
report, err := EvaluateObserved(records, target)
if err != nil {
return Report{}, err
}
report.EvidenceLimitations = append([]string{
"observation population exactly joined to supplied request trace by request ID and body SHA-256",
}, report.EvidenceLimitations...)
return report, nil
}
// ObservationReadLimits bounds JSONL evidence decoding.
type ObservationReadLimits struct {
MaxLineBytes int
MaxRecords int
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Replay with byte-identical bodies: pass the trace's raw body bytes through untouched.
- Recompute the trace digest from the exact bytes the replay sends, or regenerate both together.
- Remove intermediaries that normalize/reformat JSON between trace capture and observation.
Defensive patterns
Strategy: validation
Validate before calling
func digestsAlign(records []ObservationRecord, trace []TraceRecord) error {
byID := map[string]TraceRecord{}
for _, t := range trace {
byID[t.RequestID] = t
}
for _, r := range records {
if t, ok := byID[r.RequestID]; ok && r.RequestBodySHA256 != t.BodySHA256 {
return fmt.Errorf("observation %q body digest mismatch", r.RequestID)
}
}
return nil
} Prevention
- Replay bodies as raw bytes from the trace; never re-marshal them.
- Keep bodyDigest as the single digest implementation everywhere.
- Beware Go map iteration order if you ever rebuild bodies from maps.
When it happens
Trigger: EvaluateObservedAgainstTrace where record.RequestBodySHA256 is empty or differs from request.BodySHA256 — e.g. the replay sent a re-serialized body (key order or whitespace changed) after the trace digest was computed.
Common situations: Body re-encoding between trace generation and replay (map iteration order in Go, JSON pretty-printers, proxy reformatting); observations captured against a different corpus revision than the trace.
Related errors
- cachebench: trace request %q has invalid identity, body, or
- cachebench: session %q request %d: %w
- cachebench: observation population %d does not match trace p
- cachebench: duplicate trace request %q
- cachebench: observation request %q absent from trace
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/df46deb2df3efab0.
Report an issue: GitHub.