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

  1. Filter replication lag records before feeding the throttler: keep only TabletType_REPLICA and TabletType_RDONLY.
  2. Check that the Target protobuf in lagRecordNow is fully populated (a missing TabletType defaults to UNKNOWN).
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/8b4d6ee91af9bb3b. Report an issue: GitHub.