JuliusBrussee/caveman · error
cacheengine: nil engine
Error message
cacheengine: nil engine
What it means
Returned by (*Engine).Plan when the receiver is nil or incompletely initialized (nil guard or nil prefixSafety cache). Because Engine methods are value-safe on nil receivers in this codebase's style, this error signals the caller constructed Engine{} directly or lost the NewEngine result (including the NewEngine-error path) instead of obtaining a fully initialized engine.
Source
Thrown at cacheengine/engine.go:105
if provider == "" || driver == nil {
return nil, errors.New("cacheengine: driver needs non-empty provider and implementation")
}
if _, exists := drivers[provider]; exists {
return nil, fmt.Errorf("cacheengine: duplicate normalized driver provider %q", provider)
}
drivers[provider] = driver
}
return &Engine{
guard: cacheguard.New(), prefixSafety: newPrefixSafetyCache(8192),
maxKeyShards: maxShards, maxRequestBytes: maxRequestBytes, maxStablePrefixBytes: maxStablePrefixBytes,
resolveProfile: resolver, drivers: drivers,
}, nil
}
// Plan selects profitable stable-prefix cache boundaries without editing wire bytes.
func (e *Engine) Plan(request PlanRequest) (Plan, error) {
if e == nil || e.guard == nil || e.prefixSafety == nil {
return Plan{}, errors.New("cacheengine: nil engine")
}
if e.configErr != nil {
return Plan{}, e.configErr
}
if err := validatePlanRequest(request); err != nil {
return Plan{}, err
}
profile := normalizedProfile(request.Profile)
plan := Plan{
Decision: DecisionPassThrough,
Reason: ReasonUnsupported,
ProfileID: profile.ID,
Mode: profile.Mode,
Attribution: profile.Attribution,
EconomicsBasis: "modeled_input_rate_units",
KeyShardCount: 1,
}
if !profile.EconomicsKnown {View on GitHub (pinned to 27d5a3981a)
Solutions
- Always construct via engine, err := cacheengine.NewEngine(cfg) and return/abort on err before using engine
- Audit for zero-value Engine{} literals — they never pass the guard/prefixSafety nil checks
- If DI wiring leaves it nil, fail construction of the parent component too
Example fix
// before
e, _ := cacheengine.NewEngine(cfg)
plan, err := e.Plan(req) // e is nil when cfg was invalid
// after
e, err := cacheengine.NewEngine(cfg)
if err != nil {
return err
}
plan, err := e.Plan(req) Defensive patterns
Strategy: type-guard
Type guard
func engineReady(e *cacheengine.Engine) bool {
return e != nil
} Try / catch
plan, err := eng.Plan(req)
if err != nil {
if err.Error() == "cacheengine: nil engine" {
return plan, errors.New("engine not initialized; NewEngine failed or was skipped")
}
return plan, err
} Prevention
- Treat NewEngine's error as fatal at service construction; never continue with the nil value
- Initialize the engine eagerly at startup and add a synthetic Plan call to the health check
When it happens
Trigger: Calling Plan on var e *Engine after NewEngine returned an error and the nil engine was used anyway; copying Engine{} as a zero-value struct; storing the engine in a field that a failed initializer left nil.
Common situations: Ignoring the error from cacheengine.NewEngine and proceeding with the nil result; dependency-injection wiring that leaves the engine field nil when construction fails; races where the engine is used before initialization completes.
Related errors
- cacheengine: driver needs non-empty provider and implementat
- cachebench: nil cache engine
- cacheengine: no stable prefix
- cacheengine: invalid scope
- cacheengine: invalid epoch
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/83f589ccd1a1e3f7.
Report an issue: GitHub.