JuliusBrussee/caveman · error

cacheengine: driver needs non-empty provider and implementat

Error message

cacheengine: driver needs non-empty provider and implementation

What it means

Returned by cacheengine.NewEngine when the config.Drivers map contains an entry whose key trims/lower-cases to an empty provider string or whose Driver value is nil. Provider names are normalized (TrimSpace + ToLower) before this check, so a key of " " fails just like "". Each provider must map to a real Driver implementation before the Engine is constructed.

Source

Thrown at cacheengine/engine.go:88

		maxShards = 64
	}
	maxRequestBytes := config.MaxRequestBytes
	if maxRequestBytes == 0 {
		maxRequestBytes = defaultInputByteLimit
	}
	maxStablePrefixBytes := config.MaxStablePrefixBytes
	if maxStablePrefixBytes == 0 {
		maxStablePrefixBytes = defaultInputByteLimit
	}
	resolver := config.ResolveProfile
	if resolver == nil {
		resolver = defaultProfile
	}
	drivers := make(map[string]Driver, len(config.Drivers))
	for rawProvider, driver := range config.Drivers {
		provider := strings.ToLower(strings.TrimSpace(rawProvider))
		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")
	}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Ensure every Drivers map key is a non-blank provider name and every value is a non-nil Driver
  2. Check factory errors before inserting: if constructor returns nil driver with err, skip/abort instead of storing nil
  3. Filter empty keys when building the map from external configuration
  4. Watch for typed-nil interfaces: a (*myDriver)(nil) stored in a Driver interface is non-nil to ==nil only if the concrete type is nil pointer stored directly — store only successfully constructed values

Example fix

// before
if drv, err := newDriver(p); err != nil {
    log.Println(err) // but still stores nil below in some path
}
drivers[p] = drv

// after
if drv, err := newDriver(p); err != nil {
    return fmt.Errorf("provider %s: %w", p, err)
}
if strings.TrimSpace(p) == "" || drv == nil {
    return errors.New("empty provider or nil driver")
}
drivers[p] = drv
Defensive patterns

Strategy: validation

Validate before calling

func validDrivers(drivers map[string]cacheengine.Driver) error {
	for name, d := range drivers {
		if strings.TrimSpace(strings.ToLower(name)) == "" {
			return fmt.Errorf("blank provider name %q", name)
		}
		if d == nil {
			return fmt.Errorf("nil driver for provider %q", name)
		}
	}
	return nil
}

Type guard

func hasDrivers(cfg cacheengine.Config) bool {
	for name, d := range cfg.Drivers {
		if strings.TrimSpace(name) != "" && d != nil {
			return true
		}
	}
	return false
}

Try / catch

engine, err := cacheengine.NewEngine(cfg)
if err != nil {
	if strings.Contains(err.Error(), "driver needs non-empty provider") {
		return fmt.Errorf("driver registry misconfigured: %+v", cfg.Drivers)
	}
	return err
}

Prevention

When it happens

Trigger: Building Config{Drivers: map[string]Driver{"": drv}} or including a nil Driver value (e.g. a typed-nil from a constructor that returned nil on its own error). Constructing the map from external config where a blank provider key sneaks in.

Common situations: Registering drivers in a loop where a factory returns nil for unsupported providers and the nil is stored anyway; config-driven provider lists with an empty entry; trimmed whitespace keys like " OpenAI " are fine (they normalize) but fully-blank keys are not.

Related errors


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