{"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":null,"severity":"error","filePath":"middleware/cache/cache.go","lineNumber":681,"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\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())\n\t\te.cencoding = utils.CopyBytes(c.Response().Header.Peek(fiber.HeaderContentEncoding))\n\t\te.private = false\n\t\te.cacheControl = utils.CopyBytes(cacheControlBytes)\n\t\te.expires = utils.CopyBytes(c.Response().Header.Peek(fiber.HeaderExpires))\n\t\te.etag = utils.CopyBytes(c.Response().Header.Peek(fiber.HeaderETag))\n\t\te.date = 0","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/gofiber/fiber/blob/9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c/middleware/cache/cache.go#L663-L699","documentation":"The compound failure during eviction rollback: deleteKey of an evicted key failed (delErr) AND the subsequent attempt to restore the displaced entry's heap index (refreshHeapIndex) also failed (restoreErr). Fiber returns errors.Join of both wrapped errors so callers see the eviction delete failure and the index-restore failure. This is the worst-case eviction path — both the delete and the compensating write broke.","triggerScenarios":"cfg.MaxBytes > 0, a new write triggers eviction; the storage Delete for the chosen victim fails; the recovery loop re-adds victims to the heap and calls refreshHeapIndex which itself fails on get or set. Reproducible by making storage Delete and get/set both fail during a write that requires eviction.","commonSituations":"Storage backend (Redis) goes read/write-unavailable mid-write during high eviction churn; disk full so both Delete and set fail; TLS to storage expired; storage pool exhausted under concurrent cache pressure.","solutions":["Stabilize the storage backend immediately — both reads and writes are failing; check connectivity, capacity, and pool sizing.","Lift cfg.MaxBytes to reduce eviction frequency (set 0 for unbounded) while the backend recovers.","Scale the cache backend (cluster size, throughput) to match write load.","After recovery, flush storage and restart so heap indices and stored entries are consistent."],"exampleFix":"// before: bounded cache + failing storage -> joined delete/restore error\ncfg := cache.Config{Storage: failingRedis, MaxBytes: 1 << 16}\n\n// after: unbounded budget while backend recovers + healthy storage\ncfg := cache.Config{\n  Storage:    healthyRedis,\n  MaxBytes:   0, // or a much larger budget\n  Expiration: 10 * time.Minute,\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight write+delete check against storage before enabling bounded cache.\nfunc pingStorageFull(s fiber.Storage) error {\n    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n    defer cancel()\n    if err := s.Set(ctx, \"__p__\", []byte(\"1\"), 5*time.Second); err != nil {\n        return err\n    }\n    return s.Delete(ctx, \"__p__\")\n}","typeGuard":"null","tryCatchPattern":"app.Use(func(c fiber.Ctx) error {\n    err := c.Next()\n    if err != nil && strings.Contains(err.Error(), \"while evicting\") {\n        // compound eviction failure — storage is degraded\n        log.Errorf(\"cache eviction compound failure: %v\", err)\n        return c.Status(fiber.StatusServiceUnavailable).SendString(\"cache degraded\")\n    }\n    return err\n})","preventionTips":["Monitor storage health and fail fast at startup if writes/deletes are unhealthy.","Raise cfg.MaxBytes (or set 0 for unbounded) to lower eviction churn.","Scale the storage backend to match write load and concurrency.","Flush + restart after any sustained storage outage to resync heap and storage."],"tags":["cache","storage","eviction","delete","errors-join","bookkeeping"],"analyzedSha":"9a4c7e57fe0b080a04235d28a4b0d2b4b353d58c","analyzedAt":"2026-08-04T21:44:03.395Z","schemaVersion":2}