thanos-io/thanos · warning

too many samples

Error message

too many samples

What it means

HTTP 413 returned when the total number of samples in the request exceeds the tenant's per-request sample limit enforced by requestLimiter.AllowSamples. Like the series limit, it bounds the processing cost of a single OTLP write; totalSamples is computed by summing len(ts.Samples) over all converted time series.

Solutions

  1. Send datapoints more frequently in smaller batches instead of accumulating many points per export (prefer delta temporality or smaller batch windows).
  2. Increase the tenant's max samples per request limit.
  3. Reduce histogram bucket counts or switch to exponential histograms.
  4. Split the payload across multiple requests at the collector.

Example fix

# before: one batch with many points per series
batch:
  timeout: 30s
# after: smaller windows
batch:
  timeout: 5s
  send_batch_max_size: 2000
Defensive patterns

Strategy: validation

Validate before calling

if totalSamples > maxSamplesPerRequest { splitIntoSmallerExports() }

Try / catch

if resp.StatusCode == 413 { shrinkBatchWindow(); retry() }

Prevention

When it happens

Trigger: A single OTLP export whose total sample count (including multiple datapoints per series, e.g. cumulative histograms or dense time points) exceeds the tenant's max samples per request.

Common situations: High-frequency scrapers sending many datapoints per series; cumulative histogram buckets each counted as samples; per-request sample limit tuned too low for batched exports.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/0ff756202ca9ea15. Report an issue: GitHub.

Appendix: source

Thrown at pkg/receive/handler_otlp.go:93

	metrics, _, err := h.convertToPrometheusFormat(ctx, req.Metrics())
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	totalSamples := 0
	for _, ts := range metrics {
		totalSamples += len(ts.Samples)
	}

	if !requestLimiter.AllowSeries(tenant, int64(len(metrics))) {
		http.Error(w, "too many timeseries", http.StatusRequestEntityTooLarge)
		return
	}

	if !requestLimiter.AllowSamples(tenant, int64(totalSamples)) {
		http.Error(w, "too many samples", http.StatusRequestEntityTooLarge)
		return
	}

	rep := uint64(0)
	// If the header is empty, we assume the request is not yet replicated.
	if replicaRaw := r.Header.Get(h.options.ReplicaHeader); replicaRaw != "" {
		if rep, err = strconv.ParseUint(replicaRaw, 10, 64); err != nil {
			http.Error(w, "could not parse replica header", http.StatusBadRequest)
			return
		}
	}

	//TODO: (nicolastakashi) Handle metadata in the future.
	wreq := tprompb.WriteRequest{
		Timeseries: metrics,
	}

	// Exit early if the request contained no data. We don't support metadata yet. We also cannot fail here, because

View on GitHub (pinned to 35b8b99117)