{"record":{"id":"cf4e8f05c94c932b","repo":"go-kratos/kratos","slug":"ratelimit-cf4e8f","errorCode":"RATELIMIT","errorMessage":"service unavailable due to rate limit exceeded","messagePattern":"service unavailable due to rate limit exceeded","errorType":"http","errorClass":"github.com/go-kratos/kratos/v3/errors.Error","httpStatus":429,"severity":"error","filePath":"middleware/ratelimit/ratelimit.go","lineNumber":12,"sourceCode":"package ratelimit\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v3/errors\"\n\tinternalratelimit \"github.com/go-kratos/kratos/v3/internal/ratelimit\"\n\t\"github.com/go-kratos/kratos/v3/middleware\"\n)\n\n// ErrLimitExceed is service unavailable due to rate limit exceeded.\nvar ErrLimitExceed = errors.New(429, \"RATELIMIT\", \"service unavailable due to rate limit exceeded\")\n\n// DoneFunc records request completion.\ntype DoneFunc = internalratelimit.DoneFunc\n\n// DoneInfo contains request completion metadata.\ntype DoneInfo = internalratelimit.DoneInfo\n\n// Limiter is a rate limiter.\ntype Limiter = internalratelimit.Limiter\n\n// Option is ratelimit option.\ntype Option func(*options)\n\n// WithLimiter set Limiter implementation,\n// default is bbr limiter\nfunc WithLimiter(limiter Limiter) Option {\n\treturn func(o *options) {\n\t\to.limiter = limiter","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/go-kratos/kratos/blob/668db92c2c001e9552594ba5a8aede8456af6d7e/middleware/ratelimit/ratelimit.go#L1-L30","documentation":"ErrLimitExceed is the error the kratos ratelimit middleware returns (HTTP/gRPC status 429, reason RATELIMIT) when the configured Limiter denies a request. The middleware calls the limiter per request and, when it reports the limit is exceeded, aborts the handler and surfaces this error. It exists so a service can shed load deliberately instead of degrading latency for every caller.","triggerScenarios":"A handler or invocation wrapped by ratelimit.Server(limiter) / ratelimit.Client(limiter) calls the limiter's Allow(); the limiter (bbr or a custom implementation) reports the request exceeds the allowed rate and the middleware returns the 429 RATELIMIT error instead of invoking the handler.","commonSituations":"Traffic spikes beyond the bbr limiter's thresholds; a fixed-qps limiter sized below real traffic; per-instance limits on scaled-out deployments each enforcing their own quota; clients without backoff retrying immediately and amplifying load.","solutions":["Scale out the service or raise the limiter's budget so real traffic fits (larger bbr/ulimit settings or more replicas)","Pick the limiter strategy that matches the workload: bbr adapts to load, fixed qps does not","Make clients treat errors.Is(err, ratelimit.ErrLimitExceed) as retryable with exponential backoff and jitter","Shed load upstream (gateway/API limits) so the internal middleware rarely triggers"],"exampleFix":"// before\n// server with a limiter sized below real traffic\nsrv.Use(ratelimit.Server(limiter)) // bursts -> 429 RATELIMIT\n\n// after\n// 1) size/scale the limiter to load\nlimiter = newLimiter(higherLimit)\n// 2) client side: back off and retry on 429\nif errors.Is(err, ratelimit.ErrLimitExceed) {\n    time.Sleep(backoff(attempt))\n    return retry(ctx)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := client.Call(ctx, req)\nif err != nil {\n    if errors.Is(err, ratelimit.ErrLimitExceed) { // 429 RATELIMIT\n        time.Sleep(backoff(attempt)) // exponential + jitter\n        continue                      // retry until the retry budget is spent\n    }\n    return err\n}","preventionTips":["Load-test to size limiter thresholds before release","Give every client a retry budget with exponential backoff and jitter for 429","Scale horizontally instead of tightening per-instance limits","Shed load at the gateway so internal rate limit middleware is the last line of defense"],"tags":["rate-limit","middleware","http-429","load-shedding","go"],"backgroundTag":null,"analyzedSha":"668db92c2c001e9552594ba5a8aede8456af6d7e","analyzedAt":"2026-08-16T02:07:20.704Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}