{"record":{"id":"f592ab35be81fe2d","repo":"gofiber/fiber","slug":"limiter-failed-to-get-key-q-from-storage-w","errorCode":null,"errorMessage":"limiter: failed to get key %q from storage: %w","messagePattern":"limiter: failed to get key %q from storage: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/limiter/manager.go","lineNumber":70,"sourceCode":"// acquire returns an *entry from the sync.Pool\nfunc (m *manager) acquire() *item {\n\treturn m.pool.Get().(*item) //nolint:forcetypeassert,errcheck // We store nothing else in the pool\n}\n\n// release and reset *entry to sync.Pool\nfunc (m *manager) release(e *item) {\n\te.prevHits = 0\n\te.currHits = 0\n\te.exp = 0\n\tm.pool.Put(e)\n}\n\n// get data from storage or memory\nfunc (m *manager) get(ctx context.Context, key string) (*item, error) {\n\tif m.storage != nil {\n\t\traw, err := m.storage.GetWithContext(ctx, key)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"limiter: failed to get key %q from storage: %w\", m.logKey(key), err)\n\t\t}\n\t\tif raw != nil {\n\t\t\tit := m.acquire()\n\t\t\tif _, err := it.UnmarshalMsg(raw); err != nil {\n\t\t\t\tm.release(it)\n\t\t\t\treturn nil, fmt.Errorf(\"limiter: failed to unmarshal key %q: %w\", m.logKey(key), err)\n\t\t\t}\n\t\t\treturn it, nil\n\t\t}\n\t\treturn m.acquire(), nil\n\t}\n\n\tvalue := m.memory.Get(key)\n\tif value == nil {\n\t\treturn m.acquire(), nil\n\t}\n\n\tit, ok := value.(*item)","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/middleware/limiter/manager.go#L52-L88","documentation":"The rate-limiter manager wraps the underlying fiber.Storage backend (e.g. Redis, MySQL, SQLite, Memory) and surfaces any read failure from storage.GetWithContext as this error. The key in the message is passed through logKey(), which redacts it when shouldRedactKeys is enabled. The original storage error is preserved via %w so errors.Is/As can unwrap it.","triggerScenarios":"Calling any limiter middleware backed by a non-nil fiber.Storage when GetWithContext returns a non-nil error. Concrete triggers: Redis connection lost or DENIED, MySQL gone-away, context cancelled before the storage read completes, expired storage credentials, TLS handshake failure to the storage endpoint, or a misregistered storage driver.","commonSituations":"Production deploy where Redis was restarted or network-partitioned; storage conn-pool exhausted under burst traffic; request context already cancelled by client disconnect before the limiter runs; misconfigured STORAGE_URL env var pointing at the wrong host; storage schema migration that broke the previous connection string.","solutions":["Inspect the wrapped error with errors.Unwrap to identify the storage-layer cause (e.g. redis: connection refused, context deadline exceeded).","Verify the storage backend is reachable from the app host: redis-cli ping, mysql ping, or the storage driver's health check.","Check that the fiber.Storage instance passed to limiter.New was successfully initialized and its connection pool is sized for peak RPS (each limiter check does at least one Get + one Set).","If context cancellation is the cause, move the limiter earlier in the chain or raise the request timeout so storage I/O can complete.","Enable storage-level retries / connection healing on the storage implementation itself (the limiter does not retry)."],"exampleFix":"// before: storage wired but unchecked, errors bubble as 500\napp.Use(limiter.New(limiter.Config{Storage: redisStore}))\n\n// after: confirm storage readiness at startup and log the wrapped cause\nif err := redisStore.Ping(); err != nil { log.Fatalf(\"limiter storage unreachable: %v\", err) }\napp.Use(limiter.New(limiter.Config{Storage: redisStore}))","handlingStrategy":"retry","validationCode":"// Before mounting the limiter, probe the storage backend.\nif err := store.Ping(); err != nil {\n    return fmt.Errorf(\"limiter storage unhealthy: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"// In the handler, fall back gracefully on storage errors.\nif err := c.Next(); err != nil {\n  var le *limiter.Error // hypothetical; otherwise match on the wrapped prefix\n  if errors.Is(err, storage.ErrConn) { return c.Status(503).SendString(\"rate limit unavailable\") }\n  return err\n}","preventionTips":["Wire a health check for the storage backend into your readiness probe.","Size the storage connection pool to at least peak concurrent limiter evaluations.","Run the limiter on a storage instance dedicated to it (avoid noisy-neighbour contention).","Monitor storage latency and error rate as the limiter will surface them as 500s."],"tags":["limiter","storage","redis","rate-limiting","dependency"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}