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
- Grant DELETE permission to the storage user in addition to GET/SET.
- Avoid pointing CSRF at a read-only replica; use the writable primary.
- Log delete failures and rely on IdleTimeout TTL to reclaim the token rather than failing the request.
- Tune the storage write timeout to fit the request lifetime.
- 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
- Grant DELETE permission to the storage user.
- Point CSRF at the writable primary, never a read-only replica.
- Rely on IdleTimeout TTL as the safety net for failed deletes.
- Log delete failures distinctly to catch failover/ACL drift early.
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
- csrf: failed to fetch token from storage: %w
- csrf: failed to store token in storage: %w
- csrf: failed to delete token from storage: %w
- csrf: failed to get value from storage: %w
- csrf: failed to store key %q: %w
AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04).
Data as JSON: /data/errors/dfe2e8c0248d74cc.json.
Report an issue: GitHub.