JuliusBrussee/caveman · error
cachebench: empty stable identity
Error message
cachebench: empty stable identity
What it means
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).
Source
Thrown at cacheengine/cachebench/corpus.go:692
}
requests = append(requests, TraceRequest{
ID: fmt.Sprintf("%s/%s/%04d", provider.Provider, row.SessionID, requestNumber[row.SessionID]),
At: started.Add(elapsed[row.SessionID]), Native: native, Prefix: prefix, StableSegmentCount: stableCount,
DeclaredInputTokens: prefixTokens(prefix), MaxOutputTokens: 256,
})
}
sort.SliceStable(requests, func(i, j int) bool { return requests[i].At.Before(requests[j].At) })
return Trace{
Provider: provider,
Scenario: Scenario{Name: corpus.Metadata.Name, Turns: len(requests), AssumedTTL: 5 * time.Minute, Step: time.Second},
Requests: requests, TokenBasis: counter.Name() + " estimate over normalized OpenAI messages",
TimingBasis: TimingPerPartition,
}, nil
}
func corpusStableIdentity(messages []CorpusMessage, stableCount int) (string, error) {
if len(messages) == 0 {
return "", errors.New("cachebench: empty stable identity")
}
if stableCount == 0 {
stableCount = 1
}
if stableCount > len(messages) {
return "", errors.New("cachebench: invalid stable identity boundary")
}
digest := sha256.New()
encoder := json.NewEncoder(digest)
encoder.SetEscapeHTML(false)
for _, message := range messages[:stableCount] {
if err := encoder.Encode(message); err != nil {
return "", err
}
}
return hex.EncodeToString(digest.Sum(nil)), nil
}
View on GitHub (pinned to 27d5a3981a)
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
Example fix
// before
id, err := corpusStableIdentity(session.Messages, stableCount) // len == 0
// after
if len(session.Messages) == 0 {
continue // empty session: no cacheable prefix
}
id, err := corpusStableIdentity(session.Messages, stableCount) Defensive patterns
Strategy: validation
Validate before calling
if len(session.Messages) == 0 {
return nil, fmt.Errorf("session %s has no messages; skip or reject", session.ID)
}
id, err := corpusStableIdentity(session.Messages, stableCount) Type guard
func hasMessages(msgs []CorpusMessage) bool { return len(msgs) > 0 } Try / catch
if _, err := corpusStableIdentity(msgs, n); err != nil {
if len(msgs) == 0 {
// data bug: session with no messages; drop session and continue
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cachebench: nil corpus reader
- message exceeds byte limit
- tool message requires tool_call_id
- cachebench: invalid corpus
- cachebench: no providers
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/3e0a5a146863066a.
Report an issue: GitHub.