gofiber/fiber · warning

cache: failed to delete stale vary manifest

Error message

cache: failed to delete stale vary manifest %q: %w

What it means

Returned from the response path when, after purging an uncacheable entry (private/no-cache/Vary:*), the cache tries to also delete the associated Vary manifest and manager.del fails. The Vary manifest maps the base key to its vary-axis keys; leaving it stale would cause future requests to compute keys against an obsolete vary set, so the failure is surfaced.

Solutions

  1. Diagnose the wrapped delete error against the storage backend.
  2. Ensure the manifest key namespace is exclusive to the cache (no other writer uses the same prefix).
  3. If vary churn is high, audit upstream Vary usage to reduce axis count.
  4. Right-size storage pool and add reconnect/backoff.
  5. Reproduce with vary logging enabled to correlate manifest keys.

Example fix

// before: storage delete occasionally times out under load

// after: bound the storage client and add retries at the client level
cfg := cache.Config{Storage: storageWithRetries, DisableVaryHeaders: false}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the cache manifest key namespace is exclusive and deletes work.
func probeManifestDelete(s fiber.Storage, ns string) error {
    k := ns + ":vary:probe"
    if err := s.Set(k, []byte("{}"), time.Second); err != nil { return err }
    return s.Delete(k)
}

Try / catch

app.Use(func(c fiber.Ctx) error {
    err := c.Next()
    if err != nil && isCacheManifestDeleteErr(err) {
        // manifest cleanup failed; future vary-key computation may be stale until next purge
        return nil
    }
    return err
})

Prevention

When it happens

Trigger: cfg.DisableVaryHeaders is false and the response path determined hasVaryManifest is true (a manifest was previously stored for this base key); the storage Delete for the manifest key fails. The masked manifest key is included for correlation.

Common situations: Storage backend transient failure during a vary-axis change; multi-instance deployment racing on the same base key; manifest key namespace clash; storage under pressure during vary-heavy traffic (e.g. content negotiation).

Related errors


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

Appendix: source

Thrown at middleware/cache/cache.go:633

			if e != nil {
				if err := deleteKey(reqCtx, key); err != nil {
					if cfg.Storage != nil {
						manager.release(e)
					}
					return fmt.Errorf("cache: failed to delete cached response for key %q: %w", maskKey(key), err)
				}
				mux.Lock()
				removeHeapEntry(key, e.heapidx)
				if cfg.Storage != nil {
					manager.release(e)
				}
				e = nil
				mux.Unlock()
			}

			if !cfg.DisableVaryHeaders && hasVaryManifest {
				if err := manager.del(reqCtx, manifestKey); err != nil {
					return fmt.Errorf("cache: failed to delete stale vary manifest %q: %w", maskKey(manifestKey), err)
				}
			}

			c.Set(cfg.CacheHeader, cacheUnreachable)
			return nil
		}

		shouldStoreVaryManifest := !cfg.DisableVaryHeaders && len(varyNames) > 0
		if !cfg.DisableVaryHeaders && len(varyNames) > 0 {
			if key == baseKey {
				key += buildVaryKey(varyNames, &c.Request().Header, canonical)
			}
		} else if !cfg.DisableVaryHeaders && hasVaryManifest {
			if err := manager.del(reqCtx, manifestKey); err != nil {
				return fmt.Errorf("cache: failed to delete stale vary manifest %q: %w", maskKey(manifestKey), err)
			}
		}

View on GitHub (pinned to a105acad6c)