vitessio/vitess · error
SetIdleTimeout called when timer not initialized
Error message
SetIdleTimeout called when timer not initialized
What it means
ResourcePool.SetIdleTimeout can only adjust an already-configured idle timeout because it relies on the idleTimer created at pool construction. If the pool was created with IdleTimeout == 0 (feature disabled), idleTimer is nil and the call panics. The doc comment explicitly restricts this method to pools created with an idle timeout.
Source
Thrown at go/pools/resource_pool.go:346
if capacity == 0 {
close(rp.resources)
}
return nil
}
func (rp *ResourcePool) recordWait(start time.Time) {
rp.waitCount.Add(1)
rp.waitTime.Add(time.Since(start).Nanoseconds())
if rp.logWait != nil {
rp.logWait(start)
}
}
// SetIdleTimeout sets the idle timeout. It can only be used if there was an
// idle timeout set when the pool was created.
func (rp *ResourcePool) SetIdleTimeout(idleTimeout time.Duration) {
if rp.idleTimer == nil {
panic("SetIdleTimeout called when timer not initialized")
}
rp.idleTimeout.Store(idleTimeout.Nanoseconds())
rp.idleTimer.SetInterval(idleTimeout / 10)
}
// StatsJSON returns the stats in JSON format.
func (rp *ResourcePool) StatsJSON() string {
return fmt.Sprintf(`{"Capacity": %v, "Available": %v, "Active": %v, "InUse": %v, "MaxCapacity": %v, "WaitCount": %v, "WaitTime": %v, "IdleTimeout": %v, "IdleClosed": %v, "MaxLifetimeClosed": %v, "Exhausted": %v}`,
rp.Capacity(),
rp.Available(),
rp.Active(),
rp.InUse(),
rp.MaxCap(),
rp.WaitCount(),
rp.WaitTime().Nanoseconds(),
rp.IdleTimeout().Nanoseconds(),
rp.IdleClosed(),View on GitHub (pinned to 01a25a7d17)
Solutions
- Set a nonzero IdleTimeout when constructing the ResourcePool so idleTimer is initialized.
- Recreate the pool with the new idle timeout instead of mutating it at runtime.
- Guard calls: only invoke SetIdleTimeout when the pool was created with an idle timeout.
Example fix
// before pool := pools.NewResourcePool(f, cap, cap, 0) // no idle timeout pool.SetIdleTimeout(30 * time.Second) // panics // after pool := pools.NewResourcePool(f, cap, cap, 30*time.Second) // idle timeout set at creation pool.SetIdleTimeout(60 * time.Second) // OK
Defensive patterns
Strategy: validation
Validate before calling
if rp.IDleTimeout == 0 { // pool created without idle timeout
return errors.New("SetIdleTimeout requires a pool created with an idle timeout")
} Try / catch
defer func() { if r := recover(); r != nil { err = fmt.Errorf("SetIdleTimeout failed: %v", r) } }() Prevention
- Always pass a nonzero IdleTimeout to NewResourcePool if runtime changes are planned.
- Recreate the pool rather than toggling idle timeout from zero.
- Gate config-driven SetIdleTimeout calls on the original pool config.
When it happens
Trigger: Creating a pool via NewResourcePool (or NewGenericResourcePool) without an idle timeout (idleTimeout=0), then later calling SetIdleTimeout to enable it — e.g. reacting to a runtime config change in a service wrapping pools.
Common situations: Hot-reloading timeouts from a config file where the new config enables an idle timeout that wasn't set at startup; test code (TestIdleTimeout) versus production config drift; copying pool setup code that drops the timeout parameter.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- --s3-backup-storage-bucket required
- direct DDL is disabled
- online DDL is disabled
- --batch-size requires 'direct' ddl_strategy
- --batch-size conflicts with --uuid-list. Batching does not s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/2a094431f2279caa.
Report an issue: GitHub.