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
- Ensure records are added in monotonically increasing time order, one per interval.
- 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).
- 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
- Feed records in strictly increasing time order, one per interval
- Drop stale samples produced after a backward clock step (NTP)
- In tests, derive record times from a base truncated to the interval
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
- BUG: cannot add record because it does not start at the begi
- BUG: replicationLagCache did not return the lagRecord for cu
- BUG: invalid TabletType forwarded: %v
- rate recalculation was triggered with a zero replication lag
- BUG: invalid state: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/1d9f4531520c6488.
Report an issue: GitHub.