gofiber/fiber · warning

cache: failed to delete raw key

Error message

cache: failed to delete raw key %q after store error: %w

What it means

Raised inside the cache store path when, after the metadata entry write fails, the best-effort cleanup tries to delete the already-written raw body key (key+"_body") and that deletion also fails. The cleanup error is joined to the original store error via errors.Join, so the returned error carries both. It indicates a degraded external Storage during a cache write — the raw body is now orphaned with no metadata pointing to it.

Solutions

  1. Inspect the wrapped inner error (errors.Is/As) — the underlying Storage driver error names the real cause (e.g. redis: connection refused, quota exceeded); fix that first.
  2. Treat this as non-fatal for the request: the cache write already failed, so log the joined cleanup error and let the request proceed uncached (the handler still ran).
  3. Run periodic eviction of orphaned *_body keys on external Storage (TTL/expiry on raw keys) so a failed cleanup does not leak indefinitely.
  4. If the error is recurring, verify Storage health, connection pool sizing, and that the configured expiration is respected by the backend.

Example fix

// before: letting the joined error abort the response
if err := manager.set(reqCtx, key, e, exp); err != nil {
    return err // cleanup failure is surfaced as the response error
}

// after: cache is advisory — downgrade cleanup failures to logs
if err := manager.set(reqCtx, key, e, exp); err != nil {
    log.Warn("cache store failed (cleanup may have leaked raw body):", err)
    // continue serving from handler, not from cache
}
Defensive patterns

Strategy: try-catch

Try / catch

// cleanupOnStoreError returns a JOINED error (original + cleanup).
// Unwrap and log each; never let it fail the user response.
if err := storeCachedResponse(...); err != nil {
    log.Warn("cache store path errored:", err)
    for ub := err; ub != nil; {
        if u, ok := ub.(interface{ Unwrap() []error }); ok {
            for _, e := range u.Unwrap() {
                log.Warn(" - ", e)
            }
            break
        }
        if u, ok := ub.(interface{ Unwrap() error }); ok {
            ub = u.Unwrap()
        } else {
            break
        }
    }
    // proceed uncached
}

Prevention

When it happens

Trigger: Produced only when cfg.Storage is set, manager.setRaw(key+"_body") succeeded but the following manager.set(key) failed, and then manager.del(key+"_body") in cleanupOnStoreError also returned an error. Requires a Storage backend that fails SetWithContext/DeleteWithContext mid-operation (Redis disconnect, SQLite I/O error, storage quota hit).

Common situations: External Storage (Redis, MongoDB, SQLite) hitting a transient outage or max-memory eviction mid write; storage quota/size limit exceeded on the second write; network partition between app and storage that surfaces between the two writes. Rare under memory Storage because that path sets rawStored=false.

Related errors


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

Appendix: source

Thrown at middleware/cache/cache.go:965

			spaceReserved = false // Clear flag to prevent defer from unreserving
			mux.Unlock()
		}

		cleanupOnStoreError := func(ctx context.Context, releaseEntry, rawStored bool) error {
			var cleanupErr error
			if cfg.MaxBytes > 0 {
				mux.Lock()
				_, size := heap.remove(heapIdx)
				storedBytes -= size
				mux.Unlock()
			}
			if releaseEntry {
				manager.release(e)
			}
			if rawStored {
				rawKey := key + "_body"
				if err := manager.del(ctx, rawKey); err != nil {
					cleanupErr = errors.Join(cleanupErr, fmt.Errorf("cache: failed to delete raw key %q after store error: %w", maskKey(rawKey), err))
				}
			}
			return cleanupErr
		}

		// For external Storage we store raw body separated
		if cfg.Storage != nil {
			if err := manager.setRaw(reqCtx, key+"_body", e.body, storageExpiration); err != nil {
				if cleanupErr := cleanupOnStoreError(reqCtx, true, false); cleanupErr != nil {
					err = errors.Join(err, cleanupErr)
				}
				return err
			}
			// avoid body msgp encoding
			e.body = nil
			if err := manager.set(reqCtx, key, e, storageExpiration); err != nil {
				if cleanupErr := cleanupOnStoreError(reqCtx, false, true); cleanupErr != nil {
					err = errors.Join(err, cleanupErr)

View on GitHub (pinned to a105acad6c)