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{}, err

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Check the error from the engine constructor and abort on failure instead of using a nil engine
  2. Pass the same engine instance you use elsewhere in the pipeline into RunCorpus
  3. 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

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


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