gofiber/fiber · warning

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

Error message

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

What it means

On the same response path as 134, after marking the response uncacheable: if Vary header tracking is enabled and a stale vary manifest previously existed for this base key, Fiber calls manager.del(manifestKey) to remove it; failure returns this error. The manifest is the per-base-key index of Vary-driven sub-partitions, so leaving it stale can cause future lookups to mis-partition.

Source

Thrown at middleware/cache/cache.go:551

			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)
			}
		} 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 9a4c7e57fe)

Solutions

  1. Diagnose the wrapped storage Delete error and stabilize the backend.
  2. If Vary partitioning isn't needed, set DisableVaryHeaders: true to skip manifest bookkeeping entirely.
  3. Flush stale manifests by clearing the storage namespace after policy changes.
  4. Reduce concurrent races by narrowing cfg.Methods / cfg.Next so only stable cacheable routes participate.

Example fix

// before: vary manifests accumulate and Delete fails
cfg := cache.Config{Storage: flakyStorage}

// after: disable vary bookkeeping where not needed + healthy storage
cfg := cache.Config{
  Storage:            robustStorage,
  DisableVaryHeaders: true,
}
Defensive patterns

Strategy: validation

Validate before calling

// If vary partitioning isn't needed, disable it to avoid manifest maintenance.
cfg := cache.Config{DisableVaryHeaders: true}

Type guard

null

Try / catch

app.Use(func(c fiber.Ctx) error {
    err := c.Next()
    if err != nil && strings.Contains(err.Error(), "failed to delete stale vary manifest") {
        log.Warnf("stale vary manifest purge failed: %v", err)
        return nil
    }
    return err
})

Prevention

When it happens

Trigger: DisableVaryHeaders is false, hasVaryManifest is true, response is uncacheable (private/no-cache/Vary:*), and manager.del of the manifestKey fails. Reproducible by storing a varied response, then sending an uncacheable response for the same base key while breaking storage Delete.

Common situations: Vary-aware caching where the upstream changes a route to private/no-cache while old vary manifests linger; remote storage Delete hiccups during the transition; concurrent requests racing to mutate the same manifestKey.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/f31003201f50bb59.json. Report an issue: GitHub.