vitessio/vitess · error
BUG: limiter was unable to reserve an event. threadThrottler
Error message
BUG: limiter was unable to reserve an event. threadThrottler: %+v, reservation:%v
What it means
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.
Source
Thrown at go/vt/throttler/thread_throttler.go:100
t.actualRateHistory.addPerThread(t.threadID, record{t.currentSecond, t.currentRate})
}
t.currentRate = 0
t.currentSecond = nowSecond
}
if t.limiter.Limit() == 0 {
// If the limit is 0 this request won't be let through. However, the caller
// should poll again in the future in case the limit has changed.
return 1 * time.Second
}
// Figure out how long to backoff: We use the limiter.ReserveN() method to reserve an event.
// The returned reservation contains the backoff delay. We cancel the reservation if the
// delay is greater than 0, since the caller is expected to call throttle() again at that time
// rather than proceed.
reservation := t.limiter.ReserveN(now, 1)
if !reservation.OK() {
panic(fmt.Sprintf("BUG: limiter was unable to reserve an event. "+
"threadThrottler: %+v, reservation:%v", t, *reservation))
}
waitDuration := reservation.DelayFrom(now)
if waitDuration <= 0 {
t.currentRate++
return NotThrottled
}
reservation.CancelAt(now)
return waitDuration
}
// setMaxRate sets the maximum rate for the next time throttle() is called.
// setMaxRate() can be called concurrently with other methods of this object.
func (t *threadThrottler) setMaxRate(newRate int64) {
t.maxRate.Store(newRate)
}
// maxRate returns the rate set by the last call to setMaxRate().View on GitHub (pinned to 01a25a7d17)
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
Example fix
// before
throttler.SetMaxRate(0)
// after
if maxRate <= 0 { maxRate = defaultMaxRate }
throttler.SetMaxRate(maxRate) Defensive patterns
Strategy: validation
Validate before calling
if maxRate <= 0 {
return fmt.Errorf("maxRate must be positive, got %d", maxRate)
}
throttler.SetMaxRate(maxRate) Type guard
func validRate(r float64) bool { return r > 0 } Try / catch
defer func() {
if r := recover(); r != nil {
log.Errorf("throttle reservation panic: %v", r)
}
}() Prevention
- Reject zero/negative max rates at config load time
- Use flag validation in PreRunE for rate flags
- Unit-test setMaxRate boundary values
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ignoring higher good rate of %v because we assume that the k
- ignoring lower bad rate of %v because such a high degradatio
- GetFuncForType does not support array types
- GetFuncForType does not support channel types
- GetFuncForType does not support function types
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/170888e493ad49b5.
Report an issue: GitHub.