{"record":{"id":"b9c5ecfb3cab7a3f","repo":"thanos-io/thanos","slug":"too-many-timeseries-b9c5ec","errorCode":null,"errorMessage":"too many timeseries","messagePattern":"too many timeseries","errorType":"http","errorClass":null,"httpStatus":413,"severity":"warning","filePath":"pkg/receive/handler_otlp.go","lineNumber":88,"sourceCode":"\tif err != nil {\n\t\tlevel.Error(h.logger).Log(\"msg\", \"Error decoding remote write request\", \"err\", err.Error())\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tmetrics, _, err := h.convertToPrometheusFormat(ctx, req.Metrics())\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\ttotalSamples := 0\n\tfor _, ts := range metrics {\n\t\ttotalSamples += len(ts.Samples)\n\t}\n\n\tif !requestLimiter.AllowSeries(tenant, int64(len(metrics))) {\n\t\thttp.Error(w, \"too many timeseries\", http.StatusRequestEntityTooLarge)\n\t\treturn\n\t}\n\n\tif !requestLimiter.AllowSamples(tenant, int64(totalSamples)) {\n\t\thttp.Error(w, \"too many samples\", http.StatusRequestEntityTooLarge)\n\t\treturn\n\t}\n\n\trep := uint64(0)\n\t// If the header is empty, we assume the request is not yet replicated.\n\tif replicaRaw := r.Header.Get(h.options.ReplicaHeader); replicaRaw != \"\" {\n\t\tif rep, err = strconv.ParseUint(replicaRaw, 10, 64); err != nil {\n\t\t\thttp.Error(w, \"could not parse replica header\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\t//TODO: (nicolastakashi) Handle metadata in the future.","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/receive/handler_otlp.go#L70-L106","documentation":"HTTP 413 returned by the OTLP handler when the number of time series in one request exceeds the tenant's per-request series limit enforced by requestLimiter.AllowSeries. This is a per-request cap, distinct from the total active-series limit, designed to bound memory needed to process a single write.","triggerScenarios":"A single OTLP export whose converted metric count exceeds the tenant's max series per request (request.series_limit / max_series).","commonSituations":"Exporter batching too many resource-metrics into one payload; many targets scraped into one export; per-request limit configured below the application's natural metric count.","solutions":["Reduce exporter batch size so each request carries fewer series.","Increase the tenant's max series per request in the request limiter config.","Split exports across multiple requests at the collector (e.g. batch/queued-retry processors with smaller batches).","Aggregate/drop unneeded metrics in an OTTL processor before exporting."],"exampleFix":"# before: one huge batch\nbatch:\n  send_batch_size: 100000\n# after\nbatch:\n  send_batch_size: 5000\n  send_batch_max_size: 5000","handlingStrategy":"validation","validationCode":"if seriesCount > maxSeriesPerRequest { splitIntoBatches(metrics, maxSeriesPerRequest) }","typeGuard":null,"tryCatchPattern":"if resp.StatusCode == 413 { reduceBatchSize(); retry() }","preventionTips":["Tune collector batch processor send_batch_max_size","Know your metric count per target and size limits above it","Alert on 413 responses from receive"],"tags":["http","limits","thanos","batching"],"backgroundTag":"payload-too-large","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}