JuliusBrussee/caveman · error
cachebench: nil cache engine
Error message
cachebench: nil cache engine
What it means
RunCorpus requires a non-nil *cacheengine.Engine because it compiles requests through that engine for cache simulation. A nil engine means there is no cache model to evaluate against, so the function refuses immediately instead of panicking later.
Source
Thrown at cacheengine/cachebench/corpus.go:439
}
}
return total
}
func corpusDigest(rows []CorpusRow) string {
hash := sha256.New()
encoder := json.NewEncoder(hash)
encoder.SetEscapeHTML(false)
for _, row := range rows {
_ = encoder.Encode(row)
}
return hex.EncodeToString(hash.Sum(nil))
}
// RunCorpus evaluates one validated public corpus across provider compilers.
func RunCorpus(ctx context.Context, engine *cacheengine.Engine, corpus AgentCorpus, providers []ProviderConfig, target Target) (Report, error) {
if engine == nil {
return Report{}, errors.New("cachebench: nil cache engine")
}
if err := validateTarget(target); err != nil {
return Report{}, err
}
if len(corpus.Rows) == 0 || corpus.SHA256 == "" || strings.TrimSpace(corpus.Metadata.Name) == "" {
return Report{}, errors.New("cachebench: invalid corpus")
}
if len(providers) == 0 {
return Report{}, errors.New("cachebench: no providers")
}
if len(providers) > 1024 {
return Report{}, errors.New("cachebench: provider population exceeds 1024")
}
counter := tokens.Default()
cachedCounter := &corpusTokenCounter{Counter: counter, counts: map[[sha256.Size]byte]int{}}
summary, err := analyzeCorpus(corpus, cachedCounter)
if err != nil {
return Report{}, errView on GitHub (pinned to 27d5a3981a)
Solutions
- Check the error from the engine constructor and abort on failure instead of using a nil engine
- Pass the same engine instance you use elsewhere in the pipeline into RunCorpus
- Add a nil-engine unit test guard so regressions in wiring fail loudly
Example fix
// before
engine, _ := cacheengine.New(cfg) // err swallowed, engine may be nil
report, err := cachebench.RunCorpus(ctx, engine, corpus, providers, target)
// after
engine, err := cacheengine.New(cfg)
if err != nil { return err }
report, err := cachebench.RunCorpus(ctx, engine, corpus, providers, target) Defensive patterns
Strategy: validation
Validate before calling
engine, err := cacheengine.New(cfg)
if err != nil {
return fmt.Errorf("engine construction failed: %w", err)
}
if engine == nil {
return fmt.Errorf("engine constructor returned nil without error")
} Type guard
func hasEngine(e *cacheengine.Engine) bool { return e != nil } Try / catch
report, err := cachebench.RunCorpus(ctx, engine, corpus, providers, target)
if err != nil {
if err.Error() == "cachebench: nil cache engine" {
return fmt.Errorf("engine wiring bug: %w", err)
}
return err
} Prevention
- Never discard the error from engine constructors; check err and use the engine only on success
- Add a startup assertion that the engine dependency is non-nil before any benchmark runs
- Initialize dependencies in one constructor so a nil engine cannot be constructed partway
When it happens
Trigger: Calling cachebench.RunCorpus(ctx, nil, corpus, providers, target) — usually because the engine constructor returned nil after a swallowed error, or a struct field was never initialized before the benchmark ran.
Common situations: Engine construction error ignored (err checked, nil engine kept); wiring the benchmark in a test where the engine dependency was omitted; refactoring that moved engine creation behind a condition that did not run.
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/e50328aa7e5a14c1.
Report an issue: GitHub.