{"record":{"id":"6491223c1aabc5be","repo":"vitessio/vitess","slug":"bug-invalid-state-v","errorCode":null,"errorMessage":"BUG: invalid state: %v","messagePattern":"BUG: invalid state: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/throttler/max_replication_lag_module.go","lineNumber":374,"sourceCode":"\n\tif !m.isReplicaUnderTest(&r, now, r.TestedState, lagRecordNow) {\n\t\tgoto logResult\n\t}\n\n\t// Process the lag record and adjust the rate.\n\tif m.replicaUnderTest != nil {\n\t\t// We're checking the same replica again. The old value is no longer needed.\n\t\tm.replicaUnderTest = nil\n\t}\n\tswitch r.TestedState {\n\tcase stateIncreaseRate:\n\t\tm.increaseRate(&r, now, lagRecordNow)\n\tcase stateDecreaseAndGuessRate:\n\t\tm.decreaseAndGuessRate(&r, now, lagRecordNow)\n\tcase stateEmergency:\n\t\tm.emergency(&r, now, lagRecordNow)\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"BUG: invalid state: %v\", r.TestedState))\n\t}\n\nlogResult:\n\tr.HighestGood = m.memory.highestGood()\n\tr.LowestBad = m.memory.lowestBad()\n\n\tif clear {\n\t\tr.Reason += clearReason\n\t}\n\n\tm.results.add(r)\n}\n\n// clearReplicaUnderTest returns true if the current \"replica under test\" should\n// be cleared e.g. because the new lag record is more severe or we did not hear\n// back from the replica under test for a while.\nfunc (m *MaxReplicationLagModule) clearReplicaUnderTest(now time.Time, testedState state, lagRecordNow replicationLagRecord) (bool, string) {\n\tif m.replicaUnderTest == nil {","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/throttler/max_replication_lag_module.go#L356-L392","documentation":"recalculateRate's state switch handles all known throttler tester states (increase rate, decrease and guess, emergency, etc.). If r.TestedState holds any value outside that set, the default branch panics, since the state machine has no defined transition. This indicates a corrupted or uninitialized state enum.","triggerScenarios":"Entering recalculateRate with a Tester whose TestedState field was never initialized or was set to an out-of-range numeric value — typically via an uninitialized struct, a bad cast, or deserialization of a corrupted state.","commonSituations":"Unit tests constructing a Tester without setting TestedState; loading persisted throttler state written by a different code version with a shifted enum; manual mutation of Tester fields.","solutions":["Initialize TestedState to a valid state (the constructor's default state) before invoking recalculateRate.","Audit any code that writes r.TestedState for out-of-range assignments.","If state is persisted/loaded across versions, validate it against the known enum set before use."],"exampleFix":"// before\nr := &Tester{}\nm.recalculateRate(lagRecord) // TestedState zero => panic\n// after\nr := newTesterForTest(... ) // sets TestedState to a valid initial state\nm.recalculateRate(lagRecord)","handlingStrategy":"validation","validationCode":"func validState(s testerState) bool {\n  switch s {\n  case stateIncreaseRate, stateDecreaseAndGuessRate, stateEmergency /* + others */ :\n    return true\n  }\n  return false\n}\n// require validState(r.TestedState) before recalculateRate","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Construct Tester objects only through their constructor so TestedState is initialized","Validate deserialized state values against the known enum before use","Avoid hand-editing Tester fields in tests"],"tags":["go","panic","throttler","state-machine"],"backgroundTag":"invalid-state-machine-state","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}