vitessio/vitess · error
BUG: cannot add record because it does not start at the begi
Error message
BUG: cannot add record because it does not start at the beginning of the interval. record: %v
What it means
intervalHistory.add requires each record's time to fall exactly on an interval boundary (time truncated by the interval must equal itself). Records that start mid-interval would corrupt the fixed-width bucketing, so add panics with this BUG message. It is a programmer-contract violation within the throttler.
Source
Thrown at go/vt/throttler/interval_history.go:54
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)
sum := 0.0
count := 0.0View on GitHub (pinned to 01a25a7d17)
Solutions
- Align the record time before calling add: record.time = rawTime.Truncate(h.interval).
- In tests, build timestamps as base.Add(n * h.interval) from a truncated base so they land on boundaries.
- Verify the interval passed to newIntervalHistory matches the granularity of the timestamps you feed it.
Example fix
// before
h.add(record{time: time.Now(), lag: lag}) // mid-interval time panics
// after
h.add(record{time: time.Now().Truncate(h.interval), lag: lag}) Defensive patterns
Strategy: validation
Validate before calling
func isAligned(t time.Time, interval time.Duration) bool {
return t.Truncate(interval).Equal(t)
}
// only call h.add when isAligned(t, h.interval) Prevention
- Truncate raw measurement timestamps to the interval before adding
- Keep the interval duration fixed for the lifetime of the history
- Build test timestamps via base.Add(n * interval) from a truncated base
When it happens
Trigger: Calling add with a record whose time is not aligned to the interval grid (e.g. interval = 1s but record.time = 12:00:00.250).
Common situations: Test code constructing record times without truncating to the interval; a producer passing raw measurement timestamps instead of interval-start-aligned timestamps; changes to the interval duration that silently break previously-aligned timestamps.
Related errors
- BUG: cannot add record because it is already covered by a pr
- 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/417a6690327acdcd.
Report an issue: GitHub.