go-kratos/kratos · error · github.com/go-kratos/kratos/v3/errors.Error

RATELIMIT

RATELIMIT

Error message

service unavailable due to rate limit exceeded

What it means

ErrLimitExceed is a typed kratos error (HTTP 429, reason RATELIMIT) defined in contrib/polaris/ratelimit.go:18 and returned by the polaris Ratelimit middleware. Inside the middleware, l.Allow(operation, args...) is called with the operation plus header/query arguments built from the request; if Allow returns an error (polaris quota denied), the middleware aborts the handler and returns ErrLimitExceed without executing business logic.

Source

Thrown at contrib/polaris/ratelimit.go:18

package polaris

import (
	"context"
	"strings"

	"github.com/go-kratos/kratos/v3/errors"
	"github.com/go-kratos/kratos/v3/middleware"
	"github.com/go-kratos/kratos/v3/middleware/ratelimit"
	"github.com/go-kratos/kratos/v3/transport"
	"github.com/go-kratos/kratos/v3/transport/http"

	"github.com/polarismesh/polaris-go/pkg/model"
)

// ErrLimitExceed is service unavailable due to rate limit exceeded.
var (
	ErrLimitExceed = errors.New(429, "RATELIMIT", "service unavailable due to rate limit exceeded")
)

// Ratelimit Request rate limit middleware
func Ratelimit(l Limiter) middleware.Middleware {
	return func(handler middleware.Handler) middleware.Handler {
		return func(ctx context.Context, req any) (reply any, err error) {
			if tr, ok := transport.FromServerContext(ctx); ok {
				var args []model.Argument
				headers := tr.RequestHeader()
				// handle header
				for _, header := range headers.Keys() {
					args = append(args, model.BuildHeaderArgument(header, headers.Get(header)))
				}
				// handle http
				if ht, ok := tr.(*http.Transport); ok {
					// url query
					for key, values := range ht.Request().URL.Query() {
						args = append(args, model.BuildQueryArgument(key, strings.Join(values, ",")))

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Client side: honor the 429, back off and retry with jitter instead of hammering
  2. Check the polaris console rate-limit rules for the operation and adjust the quota if the limit is genuinely too low
  3. Verify polaris server connectivity from the app - Allow errors on infra failure look identical to real rejections
  4. Match precisely with kratos errors: if e := errors.FromError(err); e.Reason == "RATELIMIT" && e.Code == 429

Example fix

// client-side handling
reply, err := client.Call(ctx, req)
if e := kerrors.FromError(err); e.Code == 429 && e.Reason == "RATELIMIT" {
    time.Sleep(backoff()) // exponential + jitter, then retry
    reply, err = client.Call(ctx, req)
}
Defensive patterns

Strategy: retry

Try / catch

if reply, err = handler(ctx, req); err != nil {
    if e := kerrors.FromError(err); e.Code == 429 && e.Reason == "RATELIMIT" {
        // quota denied: back off and retry with jitter; do not retry immediately
    }
}

Prevention

When it happens

Trigger: Attaching polaris.Ratelimit(l) as server middleware and receiving traffic that exceeds the quota rule configured in polaris for that operation/route; Allow also returns an error for any polaris quota API failure, so a broken polaris server connection can surface the same error. Client sees a 429 whose body encodes reason RATELIMIT.

Common situations: Load test or traffic spike tripping a polaris rate rule; rule configured per-header/per-query argument (the middleware forwards all headers and URL queries as model.Argument) so a specific caller is throttled; polaris server unreachable so every Allow fails and all requests appear rate-limited.

Understand the failure class

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/ee423471703bbc5a. Report an issue: GitHub.