{"record":{"id":"c436c188fd4c41e4","repo":"cloudflare/cloudflared","slug":"errtoomanyactiveflows","errorCode":"ErrTooManyActiveFlows","errorMessage":"too many active flows","messagePattern":"too many active flows","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"flow/limiter.go","lineNumber":13,"sourceCode":"package flow\n\nimport (\n\t\"errors\"\n\t\"sync\"\n)\n\nconst (\n\tunlimitedActiveFlows = 0\n)\n\nvar (\n\tErrTooManyActiveFlows = errors.New(\"too many active flows\")\n)\n\ntype Limiter interface {\n\t// Acquire tries to acquire a free slot for a flow, if the value of flows is already above\n\t// the maximum it returns ErrTooManyActiveFlows.\n\tAcquire(flowType string) error\n\t// Release releases a slot for a flow.\n\tRelease()\n\t// SetLimit allows to hot swap the limit value of the limiter.\n\tSetLimit(uint64)\n}\n\ntype flowLimiter struct {\n\tlimiterLock        sync.Mutex\n\tactiveFlowsCounter uint64\n\tmaxActiveFlows     uint64\n\tunlimited          bool\n}","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/cloudflare/cloudflared/blob/2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f/flow/limiter.go#L1-L31","documentation":"ErrTooManyActiveFlows is returned by flow.Limiter.Acquire when the number of currently active flows already exceeds the configured maximum. It is the flow rate-limiting signal: proxies (ProxyTCP, handleDataStream) and UDP datagram handling return or match this error to reject new flows when capacity is exhausted.","triggerScenarios":"Acquiring a new flow slot via flow.Limiter.Acquire(flowType) when active flows >= max; ProxyTCP when the remote CfTraceID triggers flow rate limiting (connection/connection_test.go:112); handleDataStream for new UDP/datagram-v2 sessions over the limit; checked in http2.go:339 via errors.Is to tag responses as flow-rate-limited.","commonSituations":"Tunnels under high connection churn (many concurrent UDP/TCP flows) hitting the configured flow cap; misconfigured or intentionally low flow limits; clients opening many short-lived flows faster than they are released; load tests tripping the limiter.","solutions":["Increase the flow limit configured on the Limiter if capacity should be higher","Ensure flows are properly released (Release called on completion/error) so slots are not leaked","Implement client-side backoff/retry with jitter when acquiring a flow fails with this error","Check http2 response meta header cfdFlowRateLimited to confirm server-side rate limiting is the cause"],"exampleFix":"// before\nif err := limiter.Acquire(\"tcp\"); err != nil {\n    return err\n}\n// after\nif err := limiter.Acquire(\"tcp\"); err != nil {\n    if errors.Is(err, cfdflow.ErrTooManyActiveFlows) {\n        time.Sleep(backoffWithJitter())\n        return retryAcquire(limiter, \"tcp\")\n    }\n    return err\n}","handlingStrategy":"retry","validationCode":"// no pre-check possible for concurrent limiter state; rely on the error\n// optionally check current usage if the limiter exposes it\n// if limiter.Active() >= limiter.Max() { backoff() }","typeGuard":"func isFlowLimited(err error) bool { return errors.Is(err, cfdflow.ErrTooManyActiveFlows) }","tryCatchPattern":"if err := limiter.Acquire(\"tcp\"); err != nil {\n    if errors.Is(err, cfdflow.ErrTooManyActiveFlows) {\n        return retryWithBackoff(ctx)\n    }\n    return err\n}","preventionTips":["Always release flow slots when a flow completes or errors","Size the flow limit to expected concurrency","Add jittered backoff on Acquire failures","Watch for the cfdFlowRateLimited response meta header to detect server-side limiting"],"tags":["rate-limiting","flows","capacity"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"2253eeeb25a44a713a4b60b8ba1e1b3f377d1a0f","analyzedAt":"2026-09-06T04:14:33.757Z","contentChangedAt":"2026-09-06T04:14:33.757Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}