gofiber/fiber · error
cache: failed to delete key
Error message
cache: failed to delete key %q while evicting: %w
What it means
Returned from the eviction routine when deleteKey fails for the key being evicted (delErr), AND at least one of the previously-restored heap-index candidates also failed (restoreErr != nil). Fiber joins the original delete error with the per-candidate restore errors so both surfaces are visible. This is the worst-case eviction failure: the eviction itself failed and rollback was incomplete.
Solutions
- Read errors.Join's joined chain: the first is the original delete error, the rest are restore errors per candidate.
- Stabilize storage first (capacity, connectivity) before tuning MaxBytes.
- Raise cfg.MaxBytes to reduce eviction pressure if memory allows.
- Audit storage maxmemory-policy and pool sizing.
- Add storage error-rate alerting so this compound failure is caught early.
Example fix
// before: very tight MaxBytes amplifies storage blips into compound failures
cfg := cache.Config{MaxBytes: 1024, Storage: flakyRedis}
// after: realistic cap + stable storage
cfg := cache.Config{MaxBytes: 64 * 1024 * 1024, Storage: stableRedis} Defensive patterns
Strategy: retry
Validate before calling
// Bound the eviction surface so compound failures are rare.
func validateCacheSizing(cfg cache.Config) error {
if cfg.MaxBytes > 0 && cfg.MaxBytes < 16*1024 {
return fmt.Errorf("MaxBytes %d too small; eviction runs on every write and amplifies storage blips", cfg.MaxBytes)
}
return nil
} Try / catch
app.Use(func(c fiber.Ctx) error {
err := c.Next()
if err != nil && isCacheEvictionErr(err) {
// compound eviction failure: degrade to bypassing cache for this request and alert
metrics.EvictionCompoundError.Inc()
return nil // serve uncached
}
return err
}) Prevention
- Size cfg.MaxBytes realistically to avoid constant eviction.
- Read errors.Join chain: original delete + per-candidate restore errors.
- Stabilize storage capacity and connectivity before tuning cache size.
- Alert on compound eviction errors as a storage-health signal.
When it happens
Trigger: The cache needs to evict to satisfy MaxBytes, the chosen key's storage Delete fails, AND when Fiber tries to put back the heap entries it had optimistically evicted, one or more of those Sets also fails. Requires storage instability sustained across multiple operations.
Common situations: Storage outage or saturation lasting longer than a single op (Redis under OOM, network partition); MaxBytes set very tight so eviction runs constantly and amplifies any storage blip into a compound failure; multi-instance contention on the same storage.
Related errors
- cache: failed to reload key
- cache: failed to restore heap index for key
- cache: failed to delete key
- failed to read response
- cache: failed to delete cached response for key
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/a596b5c6634e24a1.
Report an issue: GitHub.
Appendix: source
Thrown at middleware/cache/cache.go:774
// Re-add entries to the heap to keep expiration tracking consistent
var restored []evictionCandidate
for j := i; j < len(candidates); j++ {
candidate := candidates[j]
candidate.heapIdx = heap.put(candidate.key, candidate.exp, candidate.size)
restored = append(restored, candidate)
}
mux.Unlock()
var restoreErr error
for _, candidate := range restored {
if err := refreshHeapIndex(reqCtx, candidate); err != nil {
restoreErr = errors.Join(restoreErr, err)
}
}
if restoreErr != nil {
return errors.Join(fmt.Errorf("cache: failed to delete key %q while evicting: %w", maskKey(keyToRemove), delErr), restoreErr)
}
return fmt.Errorf("cache: failed to delete key %q while evicting: %w", maskKey(keyToRemove), delErr)
}
}
}
// Hand back whatever the lookup left holding. A stale entry survives to here
// whenever the request said no-cache or it carried no expiry, and dropping
// it would leak one pooled item per such request.
if e != nil {
manager.release(e)
}
e = manager.acquire()
// Cache response
e.body = utils.CopyBytes(c.Response().Body())
e.status = c.Response().StatusCode()
e.ctype = utils.CopyBytes(c.Response().Header.ContentType())View on GitHub (pinned to a105acad6c)