{"record":{"id":"b06a1ddc2462cfe5","repo":"JuliusBrussee/caveman","slug":"cachebench-invalid-stable-identity-boundary","errorCode":null,"errorMessage":"cachebench: invalid stable identity boundary","messagePattern":"cachebench: invalid stable identity boundary","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":698,"sourceCode":"\t}\n\tsort.SliceStable(requests, func(i, j int) bool { return requests[i].At.Before(requests[j].At) })\n\treturn Trace{\n\t\tProvider: provider,\n\t\tScenario: Scenario{Name: corpus.Metadata.Name, Turns: len(requests), AssumedTTL: 5 * time.Minute, Step: time.Second},\n\t\tRequests: requests, TokenBasis: counter.Name() + \" estimate over normalized OpenAI messages\",\n\t\tTimingBasis: TimingPerPartition,\n\t}, nil\n}\n\nfunc corpusStableIdentity(messages []CorpusMessage, stableCount int) (string, error) {\n\tif len(messages) == 0 {\n\t\treturn \"\", errors.New(\"cachebench: empty stable identity\")\n\t}\n\tif stableCount == 0 {\n\t\tstableCount = 1\n\t}\n\tif stableCount > len(messages) {\n\t\treturn \"\", errors.New(\"cachebench: invalid stable identity boundary\")\n\t}\n\tdigest := sha256.New()\n\tencoder := json.NewEncoder(digest)\n\tencoder.SetEscapeHTML(false)\n\tfor _, message := range messages[:stableCount] {\n\t\tif err := encoder.Encode(message); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn hex.EncodeToString(digest.Sum(nil)), nil\n}\n\nfunc corpusProviderBody(provider ProviderConfig, messages []CorpusMessage) ([]byte, error) {\n\tswitch provider.Provider {\n\tcase \"openai\":\n\t\treturn json.Marshal(map[string]any{\"model\": provider.Model, \"max_completion_tokens\": 256, \"messages\": messages})\n\tcase \"anthropic\":\n\t\treturn anthropicCorpusBody(provider.Model, messages)","sourceCodeStart":680,"sourceCodeEnd":716,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L680-L716","documentation":"corpusStableIdentity clamps a zero stableCount to 1 but rejects stableCount greater than the number of messages: the stable prefix must be a real leading slice of the conversation. A boundary past the end means the caller's notion of prefix length disagrees with the actual session length.","triggerScenarios":"Calling corpusStableIdentity(messages, N) with N > len(messages) — e.g. a fixed prefix window (say 10) applied to a short 3-message session, or an off-by-one where stableCount was set to len(messages)+1.","commonSituations":"Configured stable-prefix depth larger than the shortest session in the corpus; loop bounds using <= instead of <; prefix depth carried over from a different corpus with longer sessions.","solutions":["Clamp stableCount to min(stableCount, len(messages)) before calling if a shorter prefix is acceptable","Validate the configured prefix depth against the shortest session and lower it or exclude tiny sessions","Fix off-by-one in the caller computing the boundary"],"exampleFix":"// before\nid, err := corpusStableIdentity(msgs, cfg.StablePrefix) // cfg=10, len(msgs)=3\n\n// after\nn := cfg.StablePrefix\nif n > len(msgs) { n = len(msgs) }\nid, err := corpusStableIdentity(msgs, n)","handlingStrategy":"validation","validationCode":"n := stableCount\nif n > len(msgs) {\n    n = len(msgs) // or reject explicitly if a fixed depth is required\n}\nif n > len(msgs) {\n    return fmt.Errorf(\"stable prefix %d exceeds session length %d\", stableCount, len(msgs))\n}\nid, err := corpusStableIdentity(msgs, n)","typeGuard":"func validStableBoundary(msgs []CorpusMessage, n int) bool { return len(msgs) > 0 && n <= len(msgs) }","tryCatchPattern":"if _, err := corpusStableIdentity(msgs, cfg.Prefix); err != nil {\n    if cfg.Prefix > len(msgs) {\n        cfg.Prefix = len(msgs) // degrade gracefully for short sessions\n    }\n}","preventionTips":["Clamp configured prefix depth to the shortest session before running identity derivation","Use min() semantics on the boundary instead of trusting config","Add a test with sessions shorter than the configured prefix"],"tags":["go","corpus","validation","off-by-one"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}