{"record":{"id":"3e0a5a146863066a","repo":"JuliusBrussee/caveman","slug":"cachebench-empty-stable-identity","errorCode":null,"errorMessage":"cachebench: empty stable identity","messagePattern":"cachebench: empty stable identity","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cacheengine/cachebench/corpus.go","lineNumber":692,"sourceCode":"\t\t}\n\t\trequests = append(requests, TraceRequest{\n\t\t\tID: fmt.Sprintf(\"%s/%s/%04d\", provider.Provider, row.SessionID, requestNumber[row.SessionID]),\n\t\t\tAt: started.Add(elapsed[row.SessionID]), Native: native, Prefix: prefix, StableSegmentCount: stableCount,\n\t\t\tDeclaredInputTokens: prefixTokens(prefix), MaxOutputTokens: 256,\n\t\t})\n\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","sourceCodeStart":674,"sourceCodeEnd":710,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/cacheengine/cachebench/corpus.go#L674-L710","documentation":"corpusStableIdentity hashes the first stableCount messages of a session to derive a cache-prefix identity. Hashing an empty message list has no stable prefix to identify, so it refuses with 'empty stable identity' rather than returning a digest of nothing (which would collide across all empty sessions).","triggerScenarios":"A corpus session whose Requests/messages slice is empty being passed to identity derivation — usually a session constructed during grouping that ended up with zero messages, or a stable-prefix lookup on a turn-0 request before any messages exist.","commonSituations":"Corpus rows referencing a session ID with no actual message rows; off-by-one when splitting a session into prefix/suffix; trace generation encountering a session whose rows were all filtered out mid-processing.","solutions":["Skip identity derivation for sessions with zero messages (they contribute nothing to cache analysis)","Audit session grouping to ensure each SessionID has at least one row with messages","Treat an empty-messages session as a data bug in the corpus and reject it at load time with its session ID"],"exampleFix":"// before\nid, err := corpusStableIdentity(session.Messages, stableCount) // len == 0\n\n// after\nif len(session.Messages) == 0 {\n    continue // empty session: no cacheable prefix\n}\nid, err := corpusStableIdentity(session.Messages, stableCount)","handlingStrategy":"validation","validationCode":"if len(session.Messages) == 0 {\n    return nil, fmt.Errorf(\"session %s has no messages; skip or reject\", session.ID)\n}\nid, err := corpusStableIdentity(session.Messages, stableCount)","typeGuard":"func hasMessages(msgs []CorpusMessage) bool { return len(msgs) > 0 }","tryCatchPattern":"if _, err := corpusStableIdentity(msgs, n); err != nil {\n    if len(msgs) == 0 {\n        // data bug: session with no messages; drop session and continue\n    }\n}","preventionTips":["Reject session IDs with zero message rows at corpus load time","Ensure prefix-splitting logic never produces empty message slices","Assert every grouped session has >=1 message before identity derivation"],"tags":["go","corpus","validation","cache-identity"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}