vitessio/vitess · error

BUG: cannot add record because it is already covered by a pr

Error message

BUG: cannot add record because it is already covered by a previous entry. record: %v next expected interval start: %v

What it means

intervalHistory.add maintains a strictly ordered series of fixed-width time intervals. If a record's time is earlier than h.nextIntervalStart, it would overlap an already-recorded interval, so add panics with this BUG message. It is an internal invariant violation, not a user-facing error.

Source

Thrown at go/vt/throttler/interval_history.go:51

type intervalHistory struct {
	records           []record
	interval          time.Duration
	nextIntervalStart time.Time
}

func newIntervalHistory(capacity int64, interval time.Duration) *intervalHistory {
	return &intervalHistory{
		records:  make([]record, 0, capacity),
		interval: interval,
	}
}

// add
// It is up to the programmer to ensure that two add() calls do not cover the
// same interval.
func (h *intervalHistory) add(record record) {
	if record.time.Before(h.nextIntervalStart) {
		panic(fmt.Sprintf("BUG: cannot add record because it is already covered by a previous entry. record: %v next expected interval start: %v", record, h.nextIntervalStart))
	}
	if !record.time.Truncate(h.interval).Equal(record.time) {
		panic(fmt.Sprintf("BUG: cannot add record because it does not start at the beginning of the interval. record: %v", record))
	}
	// TODO(mberlin): Bound the list.
	h.records = append(h.records, record)
	h.nextIntervalStart = record.time.Add(h.interval)
}

// average returns the average value across all observations which span
// the range [from, to).
// Partially included observations are accounted by their included fraction.
// Missing observations are assumed with the value zero.
func (h *intervalHistory) average(from, to time.Time) float64 {
	// Search only entries whose time of observation is in [start, end).
	// Example: [from, to) = [1.5s, 2.5s) => [start, end) = [1s, 2s)
	start := from.Truncate(h.interval)
	end := to.Truncate(h.interval)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Ensure records are added in monotonically increasing time order, one per interval.
  2. Drop or merge any record whose time is before nextIntervalStart instead of calling add for it (the caller in the throttler already filters; check for a bypass).
  3. If system clock jumps are suspected, harden the producer to skip stale samples after a backward clock step.

Example fix

// before
h.add(record{time: t, lag: lag}) // t may overlap prior interval
// after
if !t.Before(h.nextIntervalStart) && t.Truncate(h.interval).Equal(t) {
  h.add(record{time: t, lag: lag})
}
Defensive patterns

Strategy: validation

Validate before calling

func canAdd(h *intervalHistory, t time.Time) bool {
  // exposed via the package's own test seam; replicate the invariant:
  return !t.Before(h.nextIntervalStart)
}

Prevention

When it happens

Trigger: Calling add with a record whose time predates the next expected interval start — e.g. adding two records whose times fall in the same interval, or adding records out of chronological order.

Common situations: Feeding throttler replication-lag records that were buffered and arrive out of order; clock adjustments (NTP step back) producing a record time in the past; test code adding records with duplicate timestamps.

Related errors


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