gofiber/fiber · warning

cache: failed to reload key

Error message

cache: failed to reload key %q after eviction failure: %w

What it means

Returned by refreshHeapIndex inside the cache middleware when, during eviction rollback, manager.get fails to reload an entry that was being evicted. The cache keeps a min-heap of entries by expiry; when an eviction has to be undone, the entry is re-fetched. errCacheMiss is treated as benign (the key was concurrently deleted); any other storage error is wrapped here with the masked key.

Solutions

  1. Investigate the wrapped storage error: it carries the backend's message.
  2. Tighten storage health: connection pool sizing, timeouts, TLS to Redis/MySQL.
  3. If multi-instance, expect occasional errCacheMiss and ensure they are filtered (they are here, but other backend errors are not).
  4. Add storage instrumentation (latency, error rate) to catch the underlying cause.
  5. Consider cfg.MaxBytes sizing so eviction storms are rare.

Example fix

// before: shared Redis with no timeout, intermittent resets
cfg := cache.Config{Storage: myRedis}

// after: bounded Redis client with timeouts and pool caps
cfg := cache.Config{Storage: rueidisWithTimeouts}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before enabling cache with shared storage, probe health.
func probeStorage(s fiber.Storage) error {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    if err := s.Set("fiber:health", []byte("ok"), time.Second); err != nil { return err }
    _, err = s.Get("fiber:health")
    return err
}

Try / catch

// cache middleware surfaces storage errors via the configured error handler.
// Add a recovery middleware that treats cache storage errors as non-fatal and serves a fresh (uncached) response.
app.Use(func(c fiber.Ctx) error {
    err := c.Next()
    if err != nil {
        if isCacheStorageErr(err) { // match the "cache: " prefix
            _ = c.Response().Reset()
            return nil // let downstream handlers re-run without cache
        }
    }
    return err
})

Prevention

When it happens

Trigger: An external Redis/MySQL/storage backend used as cfg.Storage returns a transient error (connection reset, timeout, context cancellation) exactly when the cache tries to re-read an entry it had just selected for eviction. Concurrent expiration from another Fiber instance in a multi-node deployment can also remove the entry.

Common situations: Multi-instance Fiber deployment sharing a Redis storage with flaky networking; storage under memory pressure returning errors; a request whose context was canceled (client disconnect) reaching the eviction-rollback path; storage backend restarting mid-request.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/763130c48f4fdd67. Report an issue: GitHub.

Appendix: source

Thrown at middleware/cache/cache.go:211

			return
		}

		entry := heap.entries[indexedIdx]
		if entry.idx != heapIdx || entry.key != entryKey {
			return
		}

		_, size := heap.remove(heapIdx)
		storedBytes -= size
	}

	refreshHeapIndex := func(ctx context.Context, candidate evictionCandidate) error {
		entry, err := manager.get(ctx, candidate.key)
		if err != nil {
			if errors.Is(err, errCacheMiss) {
				return nil
			}
			return fmt.Errorf("cache: failed to reload key %q after eviction failure: %w", maskKey(candidate.key), err)
		}

		entry.heapidx = candidate.heapIdx

		remainingTTL := max(secondsToTime(entry.exp).Sub(cfg.now()), 0)

		if err := manager.set(ctx, candidate.key, entry, remainingTTL); err != nil {
			return fmt.Errorf("cache: failed to restore heap index for key %q: %w", maskKey(candidate.key), err)
		}

		return nil
	}

	// Return new handler
	return func(c fiber.Ctx) error {
		// Every field line, like the hash below: an empty first Authorization line
		// would otherwise read as anonymous. canonical is app config, fixed for the
		// life of the handler, so read it once.

View on GitHub (pinned to a105acad6c)