{"record":{"id":"a95e77759d3bae72","repo":"JuliusBrussee/caveman","slug":"cacheengine-negative-segment-measurement","errorCode":null,"errorMessage":"cacheengine: negative segment measurement","messagePattern":"cacheengine: negative segment measurement","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/engine.go","lineNumber":336,"sourceCode":"\t\tprofile.Attribution = AttributionNone\n\t}\n\treturn profile\n}\n\nfunc stablePrefix(segments []Segment, maxBytes int) ([]byte, []Segment, error) {\n\tvar prefix []byte\n\tvar stable []Segment\n\tseenNames := map[string]bool{}\n\tfor _, segment := range segments {\n\t\tif !segment.Stable || !segment.Cacheable {\n\t\t\tbreak\n\t\t}\n\t\tif !validIdentity(segment.Name, 1024, false) || len(segment.Content) == 0 || seenNames[segment.Name] {\n\t\t\treturn nil, nil, errors.New(\"cacheengine: stable segment needs name and content\")\n\t\t}\n\t\tseenNames[segment.Name] = true\n\t\tif segment.Tokens < 0 || segment.ExpectedCalls < 0 {\n\t\t\treturn nil, nil, errors.New(\"cacheengine: negative segment measurement\")\n\t\t}\n\t\tif 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) {\n\t\t\treturn nil, nil, errors.New(\"cacheengine: stable prefix exceeds configured byte limit\")\n\t\t}\n\t\tif len(segment.Name) > math.MaxUint32 || len(segment.Content) > math.MaxUint32 {\n\t\t\treturn nil, nil, errors.New(\"cacheengine: segment exceeds framing limit\")\n\t\t}\n\t\tprefix = appendFrame(prefix, segment.Name, segment.Content)\n\t\tstable = append(stable, segment)\n\t}\n\treturn prefix, stable, nil\n}\n\nfunc appendFrame(dst []byte, name string, content []byte) []byte {\n\tvar lengths [8]byte\n\tbinary.BigEndian.PutUint32(lengths[:4], uint32(len(name)))\n\tbinary.BigEndian.PutUint32(lengths[4:], uint32(len(content)))\n\tdst = append(dst, lengths[:]...)","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/engine.go#L318-L354","documentation":"Thrown by stablePrefix when a leading Stable && Cacheable segment has Tokens < 0 or ExpectedCalls < 0. Token counts and reuse estimates feed breakpoint sizing, so negative measurements are rejected outright.","triggerScenarios":"Passing a stable segment whose Tokens was set by a tokenizer that returned -1 on failure, or whose ExpectedCalls is negative after a metrics delta computation.","commonSituations":"Using -1 as a tokenizer error sentinel and forwarding it into the segment; estimating expected calls by subtracting historical counts that cross zero; unmarshalling counters from an external API that reports -1 for 'unknown'.","solutions":["Map tokenizer failure to 0 or omit the segment instead of -1","Use 0 for 'unknown' ExpectedCalls (the engine substitutes defaultCalls when calls == 0)","Clamp externally sourced counters to >= 0 before building segments"],"exampleFix":"// before\ntok, err := tokenizer.Count(text) // returns -1 on error\nseg := cacheengine.Segment{Name: \"sys\", Stable: true, Cacheable: true, Content: b, Tokens: tok}\n\n// after\ntok := 0\nif n, err := tokenizer.Count(text); err == nil {\n    tok = n\n}\nseg := cacheengine.Segment{Name: \"sys\", Stable: true, Cacheable: true, Content: b, Tokens: tok}","handlingStrategy":"validation","validationCode":"for i, s := range segs { if s.Tokens < 0 { segs[i].Tokens = 0 }; if s.ExpectedCalls < 0 { segs[i].ExpectedCalls = 0 } }","typeGuard":"func segmentMeasurementsValid(s cacheengine.Segment) bool { return s.Tokens >= 0 && s.ExpectedCalls >= 0 }","tryCatchPattern":null,"preventionTips":["Map tokenizer errors to 0, never -1","Clamp external counters to >= 0"],"tags":["cacheengine","validation","tokens","measurements","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}