{"record":{"id":"170888e493ad49b5","repo":"vitessio/vitess","slug":"bug-limiter-was-unable-to-reserve-an-event-threa","errorCode":null,"errorMessage":"BUG: limiter was unable to reserve an event. threadThrottler: %+v, reservation:%v","messagePattern":"BUG: limiter was unable to reserve an event\\. threadThrottler: %\\+v, reservation:(.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/throttler/thread_throttler.go","lineNumber":100,"sourceCode":"\t\t\tt.actualRateHistory.addPerThread(t.threadID, record{t.currentSecond, t.currentRate})\n\t\t}\n\t\tt.currentRate = 0\n\t\tt.currentSecond = nowSecond\n\t}\n\n\tif t.limiter.Limit() == 0 {\n\t\t// If the limit is 0 this request won't be let through. However, the caller\n\t\t// should poll again in the future in case the limit has changed.\n\t\treturn 1 * time.Second\n\t}\n\n\t// Figure out how long to backoff: We use the limiter.ReserveN() method to reserve an event.\n\t// The returned reservation contains the backoff delay. We cancel the reservation if the\n\t// delay is greater than 0, since the caller is expected to call throttle() again at that time\n\t// rather than proceed.\n\treservation := t.limiter.ReserveN(now, 1)\n\tif !reservation.OK() {\n\t\tpanic(fmt.Sprintf(\"BUG: limiter was unable to reserve an event. \"+\n\t\t\t\"threadThrottler: %+v, reservation:%v\", t, *reservation))\n\t}\n\twaitDuration := reservation.DelayFrom(now)\n\tif waitDuration <= 0 {\n\t\tt.currentRate++\n\t\treturn NotThrottled\n\t}\n\treservation.CancelAt(now)\n\treturn waitDuration\n}\n\n// setMaxRate sets the maximum rate for the next time throttle() is called.\n// setMaxRate() can be called concurrently with other methods of this object.\nfunc (t *threadThrottler) setMaxRate(newRate int64) {\n\tt.maxRate.Store(newRate)\n}\n\n// maxRate returns the rate set by the last call to setMaxRate().","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/throttler/thread_throttler.go#L82-L118","documentation":"After reserving an event with the rate limiter (ReserveN), throttle() expects reservation.OK() to be true; a false OK means the limiter cannot ever grant the event (typically limiter burst/limit configured to zero or negative, or limiter in a failed state). The throttler treats this as an unrecoverable BUG and panics.","triggerScenarios":"Calling ThrottlerImpl.Throttle() on a threadThrottler whose rate.Limiter rejects the reservation — commonly after setMaxRate(0) or a limiter constructed with limit <= 0 / burst < 1.","commonSituations":"Configuring max rate to 0 via the throttler's SetMaxRate in a test or via misconfigured flags; passing negative rate values from config parsing bugs.","solutions":["Ensure setMaxRate is only called with strictly positive values","Validate rate configuration at startup (reject 0 or negative rates before creating the throttler)","If a 0-rate 'pause' is intended, gate it before calling Throttle instead of feeding it to the limiter","Inspect the panic dump's threadThrottler state to confirm the configured limit/burst"],"exampleFix":"// before\nthrottler.SetMaxRate(0)\n// after\nif maxRate <= 0 { maxRate = defaultMaxRate }\nthrottler.SetMaxRate(maxRate)","handlingStrategy":"validation","validationCode":"if maxRate <= 0 {\n    return fmt.Errorf(\"maxRate must be positive, got %d\", maxRate)\n}\nthrottler.SetMaxRate(maxRate)","typeGuard":"func validRate(r float64) bool { return r > 0 }","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Errorf(\"throttle reservation panic: %v\", r)\n    }\n}()","preventionTips":["Reject zero/negative max rates at config load time","Use flag validation in PreRunE for rate flags","Unit-test setMaxRate boundary values"],"tags":["go","panic","rate-limiting","throttler","config"],"backgroundTag":"invalid-rate-limit-config","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}