gofiber/fiber · warning

csrf: failed to delete key %q: %w

Error message

csrf: failed to delete key %q: %w

What it means

Thrown at middleware/csrf/storage_manager.go:73 by storageManager.delRaw when m.storage.DeleteWithContext fails. This is the inner layer wrapped by error 152 (csrf: failed to delete token from storage); the wrapped %w is the raw driver delete error.

Source

Thrown at middleware/csrf/storage_manager.go:73

// set data to storage or memory
func (m *storageManager) setRaw(ctx context.Context, key string, raw []byte, exp time.Duration) error {
	if m.storage != nil {
		if err := m.storage.SetWithContext(ctx, key, raw, exp); err != nil {
			return fmt.Errorf("csrf: failed to store key %q: %w", m.logKey(key), err)
		}
		return nil
	}

	m.memory.Set(key, raw, exp)
	return nil
}

// delete data from storage or memory
func (m *storageManager) delRaw(ctx context.Context, key string) error {
	if m.storage != nil {
		if err := m.storage.DeleteWithContext(ctx, key); err != nil {
			return fmt.Errorf("csrf: failed to delete key %q: %w", m.logKey(key), err)
		}
		return nil
	}

	m.memory.Delete(key)
	return nil
}

func (m *storageManager) logKey(key string) string {
	if m.shouldRedactKeys {
		return redactedKey
	}
	return key
}

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Grant DELETE permission to the storage user in addition to GET/SET.
  2. Avoid pointing CSRF at a read-only replica; use the writable primary.
  3. Log delete failures and rely on IdleTimeout TTL to reclaim the token rather than failing the request.
  4. Tune the storage write timeout to fit the request lifetime.
  5. Resolve any backend capacity/connectivity issue surfaced by the wrapped error.

Example fix

// before
app.Use(csrf.New(csrf.Config{
    Storage:        replicaOnly(), // read-only
    SingleUseToken: true,
}))

// after: point at the writable primary; tolerate delete hiccups
app.Use(csrf.New(csrf.Config{
    Storage:        writablePrimary(),
    SingleUseToken: true,
    ErrorHandler: func(c fiber.Ctx, err error) error {
        if strings.Contains(err.Error(), "failed to delete key") {
            log.Printf("csrf delete failed (TTL reclaims): %v", err)
            return c.Next()
        }
        return c.Status(fiber.StatusForbidden).SendString(err.Error())
    },
}))
Defensive patterns

Strategy: fallback

Validate before calling

// Confirm DELETE works at boot.
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_ = store.SetWithContext(ctx, "__csrf_del_probe__", []byte("+"), time.Minute)
if err := store.DeleteWithContext(ctx, "__csrf_del_probe__"); err != nil {
    log.Fatalf("csrf storage DELETE denied: %v", err)
}

Try / catch

// Best-effort delete; IdleTimeout TTL reclaims the token anyway.
if err := m.storage.DeleteWithContext(ctx, key); err != nil {
    log.Printf("csrf delete failed for %q (TTL reclaims): %v", m.logKey(key), err)
    return nil
}

Prevention

When it happens

Trigger: Single-use token consumption or explicit Handler.DeleteToken triggers Storage.DeleteWithContext, and the backend rejects it: read-only failover replica, ACL missing DELETE, OOM, ctx cancelled, network error.

Common situations: Storage failover to a read-only replica; ACLs granting GET/SET but not DELETE; backend OOM; client disconnect; transient network error.

Related errors


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