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

  1. Skip identity derivation for sessions with zero messages (they contribute nothing to cache analysis)
  2. Audit session grouping to ensure each SessionID has at least one row with messages
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/3e0a5a146863066a. Report an issue: GitHub.