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

  1. Always construct via engine, err := cacheengine.NewEngine(cfg) and return/abort on err before using engine
  2. Audit for zero-value Engine{} literals — they never pass the guard/prefixSafety nil checks
  3. 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

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


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