{"record":{"id":"be51d33461bbab88","repo":"vitessio/vitess","slug":"negative-capacity","errorCode":null,"errorMessage":"negative capacity","messagePattern":"negative capacity","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/pools/smartconnpool/pool.go","lineNumber":853,"sourceCode":"// If the capacity is smaller than the number of connections that there are\n// currently open, we'll close enough connections before returning, even if\n// that means waiting for clients to return connections to the pool.\n// If the given context times out before we've managed to close enough connections\n// an error will be returned.\nfunc (pool *ConnPool[C]) SetCapacity(ctx context.Context, newcap int64) error {\n\tpool.capacityMu.Lock()\n\tdefer pool.capacityMu.Unlock()\n\tif pool.close.Load() == nil {\n\t\treturn ErrConnPoolClosed\n\t}\n\treturn pool.setCapacity(ctx, newcap)\n}\n\n// setCapacity is the internal implementation for SetCapacity; it must be called\n// with pool.capacityMu being held\nfunc (pool *ConnPool[C]) setCapacity(ctx context.Context, newcap int64) error {\n\tif newcap < 0 {\n\t\tpanic(\"negative capacity\")\n\t}\n\n\toldcap := pool.capacity.Swap(newcap)\n\t// Skip the drain only when capacity is unchanged AND we're already at or\n\t// below the target. Otherwise we may have been left with active > newcap\n\t// by a prior call that timed out (e.g. SetCapacity(0) racing with held\n\t// conns), and CloseWithContext relies on a follow-up call here to finish\n\t// draining.\n\tif oldcap == newcap && pool.active.Load() <= newcap {\n\t\treturn nil\n\t}\n\t// update the idle count to match the new capacity if necessary\n\t// wait for connections to be returned to the pool if we're reducing the capacity.\n\tdefer pool.setIdleCount()\n\n\tconst delay = 10 * time.Millisecond\n\n\t// close connections until we're under capacity","sourceCodeStart":835,"sourceCodeEnd":871,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/pools/smartconnpool/pool.go#L835-L871","documentation":"ConnPool.setCapacity panics when asked to shrink/expand the pool to a negative capacity. Capacity is an int64 atomic and a negative value has no meaning, so the internal setter fails fast instead of corrupting pool accounting (oldcap swap, drain logic).","triggerScenarios":"Calling pool.SetCapacity(ctx, n) (or resize paths) with a negative n, e.g. n computed as current - delta where delta exceeds current, or parsing a negative config value.","commonSituations":"Config with a negative capacity value; arithmetic like SetCapacity(cap-overflow) after accounting for connections about to be released; integer underflow in autoscaling code.","solutions":["Validate the new capacity before calling SetCapacity: if newcap < 0, clamp to 0 or reject the change","Fix the computation that produced the negative value (log/inspect oldcap and delta)","Sanitize configuration at startup so negative pool sizes are rejected in PreRun/config validation"],"exampleFix":"// before\npool.SetCapacity(ctx, current-int64(released)) // may be negative\n// after\nnewCap := current - int64(released)\nif newCap < 0 {\n    newCap = 0\n}\npool.SetCapacity(ctx, newCap)","handlingStrategy":"validation","validationCode":"if newCap < 0 {\n    return fmt.Errorf(\"invalid capacity %d\", newCap)\n}\npool.SetCapacity(ctx, newCap)","typeGuard":"func validCapacity(n int64) bool { return n >= 0 }","tryCatchPattern":null,"preventionTips":["Clamp computed capacities to >= 0 before calling SetCapacity","Validate pool size config at startup and reject negatives","Never derive capacity from unvalidated arithmetic on external input"],"tags":["go","panic","connection-pool","capacity"],"backgroundTag":"pool-misconfiguration","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}