{"record":{"id":"ce0648a34f836167","repo":"go-kratos/kratos","slug":"circuitbreaker","errorCode":"CIRCUITBREAKER","errorMessage":"request failed due to circuit breaker triggered","messagePattern":"request failed due to circuit breaker triggered","errorType":"http","errorClass":"github.com/go-kratos/kratos/v3/errors.Error","httpStatus":503,"severity":"error","filePath":"middleware/circuitbreaker/circuitbreaker.go","lineNumber":14,"sourceCode":"package circuitbreaker\n\nimport (\n\t\"context\"\n\n\t\"github.com/go-kratos/kratos/v3/errors\"\n\tinternalbreaker \"github.com/go-kratos/kratos/v3/internal/circuitbreaker\"\n\t\"github.com/go-kratos/kratos/v3/internal/group\"\n\t\"github.com/go-kratos/kratos/v3/middleware\"\n\t\"github.com/go-kratos/kratos/v3/transport\"\n)\n\n// ErrNotAllowed is request failed due to circuit breaker triggered.\nvar ErrNotAllowed = errors.New(503, \"CIRCUITBREAKER\", \"request failed due to circuit breaker triggered\")\n\n// CircuitBreaker is a circuit breaker.\ntype CircuitBreaker = internalbreaker.CircuitBreaker\n\n// Option is circuit breaker option.\ntype Option func(*options)\n\n// WithBreakerFactory configures a factory used to lazily create one circuit breaker per operation.\nfunc WithBreakerFactory(factory func() CircuitBreaker) Option {\n\treturn func(o *options) {\n\t\tif factory == nil {\n\t\t\treturn\n\t\t}\n\t\to.group = group.NewGroup(factory)\n\t}\n}\n\ntype options struct {","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/go-kratos/kratos/blob/668db92c2c001e9552594ba5a8aede8456af6d7e/middleware/circuitbreaker/circuitbreaker.go#L1-L32","documentation":"ErrNotAllowed (middleware/circuitbreaker/circuitbreaker.go:14) is a typed kratos error (HTTP 503, reason CIRCUITBREAKER) returned by the Client circuitbreaker middleware. Before invoking the handler it calls breaker.Allow() for the operation (grouped per operation name from transport.FromClientContext); on failure it marks the breaker failed again (deliberately, to keep the drop ratio high per the code comment) and rejects locally with ErrNotAllowed - the request never leaves the process. Failures counted by the breaker are handler errors that are 5xx/ServiceUnavailable/GatewayTimeout.","triggerScenarios":"Wrapping a client with circuitbreaker.Client(): once the downstream error rate for an operation crosses the SRE-breaker threshold, Allow() fails and every call in the cooldown window returns ErrNotAllowed immediately. Also triggered transiently right at boundary conditions with low request volume (min-sample style gates) where a couple of 500s open the breaker.","commonSituations":"A degraded dependency returning 500/503/504 causes all calls to be rejected locally, masking the original downstream error; a single flaky endpoint opening the breaker for an operation shared by many callers; tests asserting on real transport errors but receiving the 503 CIRCUITBREAKER rejection instead; breaker never seeming to close because rejections themselves MarkFailed (by design per the NOTE comment).","solutions":["Look behind the rejection: check the target service's health/logs for the 5xx errors that opened the breaker, and fix those first","Handle the error explicitly and serve a fallback (cached/default response) during the open window","Retry later with backoff sized to the breaker cooldown rather than immediately","Tune the breaker via circuitbreaker.WithBreakerFactory to supply a breaker with more forgiving window/success-ratio settings for that operation","In tests/mocks, mark handler errors as non-5xx where appropriate so the breaker does not open spuriously"],"exampleFix":"// before\nreply, err := userClient.GetUser(ctx, req)\nif err != nil { return nil, err } // surfaces 503 CIRCUITBREAKER to callers\n\n// after\nreply, err := userClient.GetUser(ctx, req)\nif e := kerrors.FromError(err); e.Reason == \"CIRCUITBREAKER\" && e.Code == 503 {\n    return cachedUser(req.Id), nil // fallback while breaker is open\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"reply, err := handler(ctx, req)\nif e := kerrors.FromError(err); e.Code == 503 && e.Reason == \"CIRCUITBREAKER\" {\n    reply, err = serveFallback(ctx, req) // cached/default response during open window\n}\nreturn reply, err","preventionTips":["Fix the downstream 5xx root cause first - the breaker only reports it","Pair circuitbreaker.Client with a fallback path (cache, default, queue-and-retry-later)","Tune breaker parameters per operation via WithBreakerFactory instead of accepting defaults for low-traffic routes","Remember rejections also MarkFailed by design: do not expect the breaker to close while you keep retrying hard","In tests, return non-5xx errors from mocked handlers so the breaker stays closed"],"tags":["middleware","circuit-breaker","resilience","http-503"],"backgroundTag":null,"analyzedSha":"668db92c2c001e9552594ba5a8aede8456af6d7e","analyzedAt":"2026-08-16T02:07:20.704Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}