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

  1. Reduce query scope (fewer matchers, shorter time range) so fewer series are returned.
  2. Raise the limit via the relevant flag (e.g. --store.series-limit) if it is intentionally too low.
  3. Check concurrent queries sharing the limiter and stagger/throttle them.
  4. 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

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


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)