{"record":{"id":"9acaa290ab19a0ea","repo":"go-kit/kit","slug":"rate-limit-exceeded","errorCode":null,"errorMessage":"rate limit exceeded","messagePattern":"rate limit exceeded","errorType":"exception","errorClass":"ErrLimited","httpStatus":429,"severity":"warning","filePath":"ratelimit/token_bucket.go","lineNumber":12,"sourceCode":"package ratelimit\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/go-kit/kit/endpoint\"\n)\n\n// ErrLimited is returned in the request path when the rate limiter is\n// triggered and the request is rejected.\nvar ErrLimited = errors.New(\"rate limit exceeded\")\n\n// Allower dictates whether or not a request is acceptable to run.\n// The Limiter from \"golang.org/x/time/rate\" already implements this interface,\n// one is able to use that in NewErroringLimiter without any modifications.\ntype Allower interface {\n\tAllow() bool\n}\n\n// NewErroringLimiter returns an endpoint.Middleware that acts as a rate\n// limiter. Requests that would exceed the\n// maximum request rate are simply rejected with an error.\nfunc NewErroringLimiter(limit Allower) endpoint.Middleware {\n\treturn func(next endpoint.Endpoint) endpoint.Endpoint {\n\t\treturn func(ctx context.Context, request interface{}) (interface{}, error) {\n\t\t\tif !limit.Allow() {\n\t\t\t\treturn nil, ErrLimited\n\t\t\t}\n\t\t\treturn next(ctx, request)","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/go-kit/kit/blob/78fbbceece7bbcf073bee814a7772f4397ea756c/ratelimit/token_bucket.go#L1-L30","documentation":"Returned by ratelimit.NewErroringLimiter when the configured Allower (typically rate.Limiter from golang.org/x/time/rate) reports Allow() == false for the current request (token_bucket.go:27-29). It is the deliberate rejection path of the erroring rate limiter: the request would exceed the configured rate and burst, so the endpoint is never invoked.","triggerScenarios":"Sustained QPS above rate.Limit(n) with the burst bucket already drained; a traffic spike consuming the whole burst at once; limits sized per-instance while a load balancer fans traffic across many instances; a shared limiter set extremely low (e.g. rate.Every(time.Second) = 1 QPS) behind a chatty client.","commonSituations":"No client-side backoff, so rejected calls are retried immediately and amplify the load; per-process limiters in horizontally scaled deployments disagreeing with the intended global limit; healthy scraping/monitoring traffic eating the burst; choosing NewErroringLimiter where NewDelayingLimiter (throttle) was the intended behavior.","solutions":["Add client-side retry with exponential backoff and jitter, honoring any Retry-After signal","Tune the limiter: raise rate.Limit and/or Burst to match real traffic (measure p99 QPS first)","If rejecting is wrong for your UX, switch to ratelimit.NewDelayingLimiter so excess requests queue instead of failing","For a global limit across instances, use a shared store implementation (e.g. redis-based Allower) instead of an in-process limiter"],"exampleFix":"// before: hard rejection at ~1 QPS\ne := ratelimit.NewErroringLimiter(rate.NewLimiter(rate.Every(time.Second), 1))(myEndpoint)\n\n// after: limits sized for real traffic, excess requests throttled not failed\ne := ratelimit.NewDelayingLimiter(rate.NewLimiter(rate.Every(10*time.Millisecond), 100))(myEndpoint)","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"var backoff time.Duration\nfor attempt := 0; attempt < 4; attempt++ {\n\tresp, err = ep(ctx, req)\n\tif err == nil || !errors.Is(err, ratelimit.ErrLimited) {\n\t\tbreak\n\t}\n\tbackoff = nextExponentialJitter(attempt) // e.g. 50ms,100ms,200ms +/- jitter\n\ttime.Sleep(backoff)\n}","preventionTips":["Size limits from measured traffic (p99 QPS) and set Burst to absorb normal spikes","Always pair NewErroringLimiter with client-side exponential backoff + jitter","Consider NewDelayingLimiter when queueing is preferable to rejection","For multi-instance deployments use a shared (e.g. Redis-backed) Allower so the effective limit matches intent"],"tags":["go","go-kit","rate-limiting","backpressure","middleware"],"backgroundTag":null,"analyzedSha":"78fbbceece7bbcf073bee814a7772f4397ea756c","analyzedAt":"2026-08-15T22:31:35.570Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}