vitessio/vitess · error
BUG: invalid TabletType forwarded: %v
Error message
BUG: invalid TabletType forwarded: %v
What it means
MaxReplicationLagModule.lagCacheByType dispatches to the per-tablet-type lag cache (replica or rdonly). Any other TabletType reaching it is a programming bug, so it panics. Only REPLICA and RDONLY are valid inputs to the throttler's lag bookkeeping.
Source
Thrown at go/vt/throttler/max_replication_lag_module.go:286
m.lagCache(lagRecord).add(lagRecord)
m.lagCache(lagRecord).sortByLag(m.getNSlowestReplicasConfig(lagRecord), m.config.MaxReplicationLagSec+1)
m.recalculateRate(lagRecord)
}
func (m *MaxReplicationLagModule) lagCache(lagRecord replicationLagRecord) *replicationLagCache {
return m.lagCacheByType(lagRecord.Target.TabletType)
}
func (m *MaxReplicationLagModule) lagCacheByType(tabletType topodatapb.TabletType) *replicationLagCache {
switch tabletType {
case topodatapb.TabletType_REPLICA:
return m.replicaLagCache
case topodatapb.TabletType_RDONLY:
return m.rdonlyLagCache
default:
panic(fmt.Sprintf("BUG: invalid TabletType forwarded: %v", tabletType))
}
}
func (m *MaxReplicationLagModule) getNSlowestReplicasConfig(lagRecord replicationLagRecord) int {
switch lagRecord.Target.TabletType {
case topodatapb.TabletType_REPLICA:
return int(m.config.IgnoreNSlowestReplicas)
case topodatapb.TabletType_RDONLY:
return int(m.config.IgnoreNSlowestRdonlys)
default:
panic(fmt.Sprintf("BUG: invalid TabletType forwarded: %v", lagRecord))
}
}
func (m *MaxReplicationLagModule) recalculateRate(lagRecordNow replicationLagRecord) {
if lagRecordNow.isZero() {
panic("rate recalculation was triggered with a zero replication lag record")
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Filter replication lag records before feeding the throttler: keep only TabletType_REPLICA and TabletType_RDONLY.
- Check that the Target protobuf in lagRecordNow is fully populated (a missing TabletType defaults to UNKNOWN).
- If you legitimately need another type, extend lagCacheByType and getNSlowestReplicasConfig with a new cache rather than reusing the existing dispatch.
Example fix
// before
m.lagCache(lagRecord) // panics for PRIMARY
// after
if t := lagRecord.Target.GetTabletType(); t == topodatapb.TabletType_REPLICA || t == topodatapb.TabletType_RDONLY {
m.lagCache(lagRecord)
} Defensive patterns
Strategy: validation
Validate before calling
func isReadableTabletType(t topodatapb.TabletType) bool {
return t == topodatapb.TabletType_REPLICA || t == topodatapb.TabletType_RDONLY
}
// skip records where !isReadableTabletType(lagRecord.Target.GetTabletType()) Prevention
- Filter throttler inputs to REPLICA/RDONLY before calling MaxReplicationLagModule
- Always populate Target.TabletType when constructing records from protobufs
- Remember the zero TabletType is UNKNOWN and will panic — treat it as invalid
When it happens
Trigger: Calling lagCache / MaxLag / clearReplicaUnderTest with a replicationLagRecord whose Target.TabletType is not REPLICA or RDONLY (e.g. PRIMARY, SPARE, or an unset/UNKNOWN type).
Common situations: Feeding the throttler records built from targets that were not filtered to replica/rdonly; protobuf records with TabletType unset (zero value) because the target was never populated; unit tests using PRIMARY targets.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- BUG: cannot add record because it is already covered by a pr
- BUG: cannot add record because it does not start at the begi
- rate recalculation was triggered with a zero replication lag
- BUG: invalid state: %v
- BUG: cannot compare states: %v and %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/8b4d6ee91af9bb3b.
Report an issue: GitHub.