{"record":{"id":"a596b5c6634e24a1","repo":"gofiber/fiber","slug":"cache-failed-to-delete-key-q-while-evicting-w","errorCode":null,"errorMessage":"cache: failed to delete key %q while evicting: %w","messagePattern":"cache: failed to delete key %q while evicting: %w","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"middleware/cache/cache.go","lineNumber":774,"sourceCode":"\n\t\t\t\t\t// Re-add entries to the heap to keep expiration tracking consistent\n\t\t\t\t\tvar restored []evictionCandidate\n\t\t\t\t\tfor j := i; j < len(candidates); j++ {\n\t\t\t\t\t\tcandidate := candidates[j]\n\t\t\t\t\t\tcandidate.heapIdx = heap.put(candidate.key, candidate.exp, candidate.size)\n\t\t\t\t\t\trestored = append(restored, candidate)\n\t\t\t\t\t}\n\t\t\t\t\tmux.Unlock()\n\n\t\t\t\t\tvar restoreErr error\n\t\t\t\t\tfor _, candidate := range restored {\n\t\t\t\t\t\tif err := refreshHeapIndex(reqCtx, candidate); err != nil {\n\t\t\t\t\t\t\trestoreErr = errors.Join(restoreErr, err)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif restoreErr != nil {\n\t\t\t\t\t\treturn errors.Join(fmt.Errorf(\"cache: failed to delete key %q while evicting: %w\", maskKey(keyToRemove), delErr), restoreErr)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn fmt.Errorf(\"cache: failed to delete key %q while evicting: %w\", maskKey(keyToRemove), delErr)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Hand back whatever the lookup left holding. A stale entry survives to here\n\t\t// whenever the request said no-cache or it carried no expiry, and dropping\n\t\t// it would leak one pooled item per such request.\n\t\tif e != nil {\n\t\t\tmanager.release(e)\n\t\t}\n\t\te = manager.acquire()\n\t\t// Cache response\n\t\te.body = utils.CopyBytes(c.Response().Body())\n\t\te.status = c.Response().StatusCode()\n\t\te.ctype = utils.CopyBytes(c.Response().Header.ContentType())","sourceCodeStart":756,"sourceCodeEnd":792,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/cache/cache.go#L756-L792","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: very tight MaxBytes amplifies storage blips into compound failures\ncfg := cache.Config{MaxBytes: 1024, Storage: flakyRedis}\n\n// after: realistic cap + stable storage\ncfg := cache.Config{MaxBytes: 64 * 1024 * 1024, Storage: stableRedis}","handlingStrategy":"retry","validationCode":"// Bound the eviction surface so compound failures are rare.\nfunc validateCacheSizing(cfg cache.Config) error {\n    if cfg.MaxBytes > 0 && cfg.MaxBytes < 16*1024 {\n        return fmt.Errorf(\"MaxBytes %d too small; eviction runs on every write and amplifies storage blips\", cfg.MaxBytes)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"app.Use(func(c fiber.Ctx) error {\n    err := c.Next()\n    if err != nil && isCacheEvictionErr(err) {\n        // compound eviction failure: degrade to bypassing cache for this request and alert\n        metrics.EvictionCompoundError.Inc()\n        return nil // serve uncached\n    }\n    return err\n})","preventionTips":["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."],"tags":["cache","storage","eviction","compound-error","redis","distributed"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}