thanos-io/thanos · error
limit violated (got )
Error message
limit %v violated (got %v)
What it means
Limiter.ReserveWithType atomically increments the reserved counter and returns errors.Errorf("limit %v violated (got %v)") when the reservation exceeds the configured static limit. Thanos uses these limiters (series/chunks limits) to protect the query path from unbounded memory usage.
Solutions
- Reduce query scope (fewer matchers, shorter time range) so fewer series are returned.
- Raise the limit via the relevant flag (e.g. --store.series-limit) if it is intentionally too low.
- Check concurrent queries sharing the limiter and stagger/throttle them.
- Monitor l.failedCounter metrics to see how often the limit trips.
Example fix
// before storegw: seriesLimit: 100000 // after storegw: seriesLimit: 500000 # or narrow the query's matchers
Defensive patterns
Strategy: validation
Validate before calling
// estimate series count before query via /api/v1/series or len(postings hint)
if estimatedSeries*concurrentQueries > limit { /* reduce scope or raise limit */ } Try / catch
if err != nil {
var limErr interface{ Limit() uint64 }
// handle as "limit exceeded": back off or reduce query scope
} Prevention
- Size limits from real cardinality data, not guesses
- Alert on l.failedCounter metric spikes
- Document the limits in runbooks for query authors
When it happens
Trigger: A StoreAPI Series response whose seriesCount (or chunks) would push the running total above the limit configured via NewBytesLimiter/NewChunksLimiterFactory (e.g. --store.series-limit / --store.chunk-limit flags).
Common situations: Queries selecting too many series hitting the series-limit; misconfigured (too low) limit flags; a burst of concurrent queries sharing one limiter factory exhausting the shared budget.
Related errors
- failed to send series
- failed to send samples
- failed to parse template
- failed to execute template
- failed to parse the template
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/2f6ad73ed64190fd.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/limiter.go:81
}
// Reserve implements ChunksLimiter.
func (l *Limiter) Reserve(num uint64) error {
return l.ReserveWithType(num, 0)
}
func (l *Limiter) ReserveWithType(num uint64, _ StoreDataType) error {
if l == nil {
return nil
}
if l.limit == 0 {
return nil
}
if reserved := l.reserved.Add(num); reserved > l.limit {
// We need to protect from the counter being incremented twice due to concurrency
// while calling Reserve().
l.failedOnce.Do(l.failedCounter.Inc)
return errors.Errorf("limit %v violated (got %v)", l.limit, reserved)
}
return nil
}
// NewChunksLimiterFactory makes a new ChunksLimiterFactory with a static limit.
func NewChunksLimiterFactory(limit uint64) ChunksLimiterFactory {
return func(failedCounter prometheus.Counter) ChunksLimiter {
return NewLimiter(limit, failedCounter)
}
}
// NewSeriesLimiterFactory makes a new SeriesLimiterFactory with a static limit.
func NewSeriesLimiterFactory(limit uint64) SeriesLimiterFactory {
return func(failedCounter prometheus.Counter) SeriesLimiter {
return NewLimiter(limit, failedCounter)
}
}
View on GitHub (pinned to 35b8b99117)