JuliusBrussee/caveman · error
cacheengine: segment exceeds framing limit
Error message
cacheengine: segment exceeds framing limit
What it means
Thrown by stablePrefix when len(segment.Name) or len(segment.Content) exceeds math.MaxUint32, because the 8-byte frame header encodes each length as a big-endian uint32. On 64-bit platforms this is practically unreachable (it requires a > 4 GiB name or content in a single segment) and is a defensive guard.
Source
Thrown at cacheengine/engine.go:342
var prefix []byte
var stable []Segment
seenNames := map[string]bool{}
for _, segment := range segments {
if !segment.Stable || !segment.Cacheable {
break
}
if !validIdentity(segment.Name, 1024, false) || len(segment.Content) == 0 || seenNames[segment.Name] {
return nil, nil, errors.New("cacheengine: stable segment needs name and content")
}
seenNames[segment.Name] = true
if segment.Tokens < 0 || segment.ExpectedCalls < 0 {
return nil, nil, errors.New("cacheengine: negative segment measurement")
}
if maxBytes < 8 || len(segment.Name) > maxBytes-8 || len(segment.Content) > maxBytes-8-len(segment.Name) || len(prefix) > maxBytes-8-len(segment.Name)-len(segment.Content) {
return nil, nil, errors.New("cacheengine: stable prefix exceeds configured byte limit")
}
if len(segment.Name) > math.MaxUint32 || len(segment.Content) > math.MaxUint32 {
return nil, nil, errors.New("cacheengine: segment exceeds framing limit")
}
prefix = appendFrame(prefix, segment.Name, segment.Content)
stable = append(stable, segment)
}
return prefix, stable, nil
}
func appendFrame(dst []byte, name string, content []byte) []byte {
var lengths [8]byte
binary.BigEndian.PutUint32(lengths[:4], uint32(len(name)))
binary.BigEndian.PutUint32(lengths[4:], uint32(len(content)))
dst = append(dst, lengths[:]...)
dst = append(dst, name...)
return append(dst, content...)
}
func breakpointCandidates(segments []Segment, defaultCalls int, profile Profile) ([]Breakpoint, bool, bool, error) {
var candidates []BreakpointView on GitHub (pinned to 27d5a3981a)
Solutions
- Split oversized content into multiple stable segments below the byte limit (the maxBytes check at engine.go:339 will usually fire first anyway)
- Check where the segment content is built: an unbounded io.ReadAll is almost certainly the real bug
- Add an upper bound when accumulating segment content from readers
Example fix
// before
content, _ := io.ReadAll(resp.Body) // unbounded
seg := cacheengine.Segment{Name: "corpus", Stable: true, Cacheable: true, Content: content}
// after
content, _ := io.ReadAll(io.LimitReader(resp.Body, maxSegmentBytes))
seg := cacheengine.Segment{Name: "corpus", Stable: true, Cacheable: true, Content: content} Defensive patterns
Strategy: validation
Validate before calling
for _, s := range segs { if uint64(len(s.Name)) > math.MaxUint32 || uint64(len(s.Content)) > math.MaxUint32 { return errors.New("segment too large") } } Type guard
// n/a
Prevention
- Bound readers with io.LimitReader when building segment content
- Split giant content into multiple segments
When it happens
Trigger: A single stable segment whose Name or Content byte slice is larger than 4294967295 bytes; only possible when streaming/accumulating gigantic buffers into one segment.
Common situations: Essentially never in practice; would indicate a bug where the wrong slice (e.g. an entire file corpus or an unbounded reader's output) is attached as one segment's Content.
Related errors
- cacheengine: stable prefix exceeds configured byte limit
- cacheengine: request exceeds configured byte limit
- cacheengine: unexpected delimiter
- cachebench: provider population exceeds 1024
- cachebench: invalid observation read limits
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/2b0a41f17c49bdaa.
Report an issue: GitHub.