vitessio/vitess · error
cell mode should be handled by the gateway, not the balancer
Error message
cell mode should be handled by the gateway, not the balancer factory
What it means
NewTabletBalancer is the balancer factory that maps a configured balancer Mode to a TabletBalancer implementation. ModeCell is special: cell-local routing is handled by the vtgate gateway itself, so requesting a cell balancer from this factory is explicitly rejected rather than returning a broken implementation.
Source
Thrown at go/vt/vtgate/balancer/balancer.go:165
// Supported modes:
// - "prefer-cell": Flow-based balancer that maintains cell affinity while balancing load
// - See the RFC here: https://github.com/vitessio/vitess/issues/12241
// - "random": Random balancer that uniformly distributes load without cell affinity
// - "session": Session balancer that pins a session to the same tablet for the duration of the session. If the tablet goes away, the session is automatically and transparently migrated to another tablet of the same type.
//
// Note: "cell" mode is handled by the gateway and does not create a balancer instance.
// operates as a round robin inside of the vtgate's cell
// Returns an error for unsupported modes.
func NewTabletBalancer(mode Mode, localCell string, vtGateCells []string) (TabletBalancer, error) {
switch mode {
case ModePreferCell:
return newFlowBalancer(localCell, vtGateCells), nil
case ModeRandom:
return newRandomBalancer(localCell, vtGateCells), nil
case ModeSession:
return newSessionBalancer(localCell), nil
case ModeCell:
return nil, errors.New("cell mode should be handled by the gateway, not the balancer factory")
default:
return nil, fmt.Errorf("unsupported balancer mode: %s (supported modes: %s)", mode, strings.Join(GetAvailableModeNames(), ", "))
}
}
func newFlowBalancer(localCell string, vtGateCells []string) TabletBalancer {
return &flowBalancer{
localCell: localCell,
vtGateCells: vtGateCells,
allocations: map[discovery.KeyspaceShardTabletType]*targetAllocation{},
}
}
type flowBalancer struct {
//
// Configuration
//
View on GitHub (pinned to 01a25a7d17)
Solutions
- Remove the cell mode setting and let the gateway handle cell-local routing
- Use one of the supported modes (flow, random, session) in the balancer config
- If cell-aware routing is desired, configure it at the gateway level, not via the balancer factory
Example fix
// before balancer, err := balancer.NewTabletBalancer(ctx, localCell, vtGateCells, balancer.ModeCell) // after balancer, err := balancer.NewTabletBalancer(ctx, localCell, vtGateCells, balancer.ModeRandom)
Defensive patterns
Strategy: validation
Validate before calling
if mode == balancer.ModeCell {
return errors.New("cell mode must be handled by the gateway; choose another mode")
}
balancer, err := balancer.NewTabletBalancer(ctx, localCell, vtGateCells, mode) Prevention
- Validate balancer mode against GetAvailableModeNames()/supported modes before calling the factory
- Keep cell-local routing configuration at the gateway layer
- Add a unit test covering every Mode value the factory rejects
When it happens
Trigger: Calling NewTabletBalancer with Mode==ModeCell, e.g. a config that sets the balancer mode to 'cell' when constructing a tablet balancer.
Common situations: Misreading documentation and setting the gateway balancer mode to 'cell' expecting the factory to create it; leftover config from an older setup where cell routing was configured differently.
Related errors
- unsupported balancer mode: %s (supported modes: %s)
- both the dry-run mode and actual buffering is enabled. To av
- unix sockets are not supported on windows
- Binary.ReverseMap: keyspaceId is nil
- 1105
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/c3374465d5188328.
Report an issue: GitHub.