vitessio/vitess · error
BUG: cannot compare states: %v and %v
Error message
BUG: cannot compare states: %v and %v
What it means
stateGreater defines a partial order over the throttler tester states. Comparing a state outside the handled set (or an invalid pair) hits the default branch and panics with both state values embedded. Used by clearReplicaUnderTest/isReplicaUnderTest/showThrottlerLog to rank states.
Source
Thrown at go/vt/throttler/max_replication_lag_module.go:441
// timeout.
return true, fmt.Sprintf("we didn't see a recent record from it within the last %.1f seconds", m.config.MaxDurationBetweenIncreases().Seconds())
}
return false, ""
}
// stateGreater returns true if a > b i.e. the state "a" is more severe than
// "b". For example, "decrease" > "increase" returns true.
func stateGreater(a, b state) bool {
switch a {
case stateIncreaseRate:
return false
case stateDecreaseAndGuessRate:
return b == stateIncreaseRate
case stateEmergency:
return b == stateIncreaseRate || b == stateDecreaseAndGuessRate
default:
panic(fmt.Sprintf("BUG: cannot compare states: %v and %v", a, b))
}
}
// isReplicaUnderTest returns true if a 'replica under test' is currently set
// and we should not skip the current replica ("lagRecordNow").
// Even if it's the same replica we may skip it and return false because
// we want to wait longer for the propagation of the current rate change.
func (m *MaxReplicationLagModule) isReplicaUnderTest(r *Result, now time.Time, testedState state, lagRecordNow replicationLagRecord) bool {
if m.replicaUnderTest == nil {
return true
}
if m.replicaUnderTest.key != discovery.TabletToMapKey(lagRecordNow.Tablet) {
r.Reason = fmt.Sprintf("skipping this replica because we're waiting for the next lag record from the 'replica under test': %v", m.replicaUnderTest.alias)
return false
}
if stateGreater(m.replicaUnderTest.state, testedState) {View on GitHub (pinned to 01a25a7d17)
Solutions
- Ensure TestedState is always set to a valid enumerated state before any throttler log/replica-under-test logic runs.
- Validate state values at Tester construction time so invalid values cannot propagate to stateGreater.
- Update the switch in stateGreater if a new state was added to the enum elsewhere but not here.
Example fix
// before
r := &Tester{}
stateGreater(r.TestedState, stateIncreaseRate) // panics
// after
r := newTesterForTest(... ) // initializes TestedState
stateGreater(r.TestedState, stateIncreaseRate) Defensive patterns
Strategy: validation
Validate before calling
if !validState(a) || !validState(b) {
return false // or log and skip the comparison
} Prevention
- Initialize TestedState through the Tester constructor
- When adding a new state enum value, update every switch including stateGreater
- Cover all state values in unit tests so an unhandled state fails a test, not production
When it happens
Trigger: Calling stateGreater(a, b) where a is not one of the enumerated valid states — e.g. a Tester with an uninitialized/invalid TestedState is passed by isReplicaUnderTest or clearReplicaUnderTest.
Common situations: Same source as error 1708: uninitialized Tester state, corrupted/deserialized enum values, tests constructing states manually with invalid values.
Related errors
- BUG: invalid state: %v
- BUG: cannot add record because it is already covered by a pr
- BUG: cannot add record because it does not start at the begi
- BUG: invalid TabletType forwarded: %v
- rate recalculation was triggered with a zero replication lag
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/c7f4b7db38f0581d.
Report an issue: GitHub.