Prometheus Error Guide: 'body size limit exceeded' — Trim or Raise the Scrape Cap
Fix Prometheus 'body size limit exceeded': find the oversized /metrics response, drop noisy series with relabeling, and raise body_size_limit safely without hiding a cardinality bug.
- #prometheus
- #monitoring
- #troubleshooting
- #errors
Stuck on this Prometheus & Monitoring error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
When a target’s /metrics response is larger than the job’s configured body_size_limit, Prometheus aborts the read mid-stream, discards the entire scrape, and marks the target down:
ts=2026-07-06T10:03:57.421Z caller=scrape.go:1382 level=warn component="scrape manager" scrape_pool=app-metrics target=http://10.2.9.14:9102/metrics msg="Scrape failed" err="body size limit exceeded (limit: 10485760 bytes)"
The same reason surfaces on /api/v1/targets:
"health": "down",
"lastError": "body size limit exceeded (limit: 10485760 bytes)",
"scrapeUrl": "http://10.2.9.14:9102/metrics"
body_size_limit caps the number of bytes Prometheus will read from a single scrape response (default 0 = unlimited unless set). Unlike sample_limit, which counts series after parsing, body_size_limit is enforced on the raw response body as it streams in — so an oversized payload is rejected before it can be parsed or stored. As with other scrape limits, the whole scrape is dropped, not truncated, so up goes to 0.
Symptoms
- A target flips to
downwithbody size limit exceeded (limit: N bytes)while the exporter process itself is healthy and reachable. curling the target’s/metricsreturns a large response (tens of MB) that takes a while to complete.- The failure appeared after an exporter upgrade, a new high-cardinality metric, or enabling extra collectors.
up == 0for the target, and any alerts on its series reportno data/absent().- Other, smaller targets in the same job scrape fine — only the fat ones fail.
Common Root Causes
- A genuinely huge exporter — kube-state-metrics, cAdvisor, or a custom exporter emitting hundreds of thousands of lines, legitimately exceeding a conservative byte cap.
- A cardinality explosion — an unbounded label (request IDs, raw URLs, UUIDs) multiplying series and inflating the response into the megabytes.
- A
body_size_limitset too low — a cautious global default applied uniformly to jobs of very different sizes. - Verbose metric help/type comments — exporters that emit long
# HELP/# TYPEtext per metric, bloating the body beyond what the series count alone suggests. - Accidental duplication — a misconfigured exporter exposing multiple registries or repeating metric families in one response.
Diagnostic Workflow
Find every target down for the byte-limit reason:
curl -s http://localhost:9090/api/v1/targets \
| jq -r '.data.activeTargets[] | select(.lastError|test("body size limit")) | [.scrapePool,.scrapeUrl,.lastError] | @tsv'
app-metrics http://10.2.9.14:9102/metrics body size limit exceeded (limit: 10485760 bytes)
Measure the actual response size the exporter is producing (bytes and line count):
curl -s http://10.2.9.14:9102/metrics | wc -c
curl -s http://10.2.9.14:9102/metrics | grep -vcE '^(#|$)'
14680320
118442
Rank the biggest metric families so you know what to drop:
curl -s http://10.2.9.14:9102/metrics \
| grep -vE '^#' | awk '{print $1}' | sed 's/{.*//' | sort | uniq -c | sort -rn | head
Read the configured limit from the live config:
curl -s http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' \
| grep -B2 -A6 'job_name: app-metrics' | grep body_size_limit
The example scrape config that triggers it:
# /etc/prometheus/prometheus.yml
scrape_configs:
- job_name: "app-metrics"
body_size_limit: 10MB # too small for a 14MB response
static_configs:
- targets: ["10.2.9.14:9102"]
Confirm the fix by dropping the noisiest family and reloading:
topk(10, count by (__name__) ({job="app-metrics"}))
Example Root Cause Analysis
An SRE set a global body_size_limit: 10MB as a defensive default. Weeks later a single app-metrics target went down with body size limit exceeded (limit: 10485760 bytes) while its peers stayed green.
curl … | wc -c showed the response was 14 MB and grep -vcE '^(#|$)' counted 118k lines. Ranking families revealed one custom metric, http_request_duration_seconds, carried a raw path label containing per-request IDs, producing ~90k unique series on its own.
The team did not just raise the cap. They added a metric_relabel_configs rule to rewrite the unbounded path into a bounded route template and drop the raw label, which shrank the response to 2.3 MB. Because that was a durable cardinality fix, the 10 MB cap could stay in place as a tripwire. Only after confirming the smaller body did they reload and watch the target return to up.
Had they simply raised body_size_limit to unlimited, the underlying cardinality bug would have kept growing until it hit TSDB memory limits instead — a worse failure later.
Prevention Best Practices
- Set
body_size_limiton jobs as a byte tripwire sized with headroom over the real response size, especially for large exporters like kube-state-metrics and cAdvisor. - Fix cardinality at the source: rewrite unbounded labels (paths, IDs, UUIDs) into bounded values with
metric_relabel_configsrather than raising limits forever. - Combine
body_size_limit(bytes) withsample_limit(series) — they guard different failure modes and catch different explosions. - Alert on scrape health (
up == 0) correlated with the exporter being reachable, so a limit trip is distinguishable from a network outage. - Re-evaluate byte and sample limits whenever an exporter is upgraded or new collectors are enabled.
- Prefer route templates and status classes in labels; never embed per-request identifiers.
Quick Command Reference
# Targets down on body_size_limit
curl -s http://localhost:9090/api/v1/targets \
| jq -r '.data.activeTargets[] | select(.lastError|test("body size limit")) | [.scrapeUrl,.lastError] | @tsv'
# Actual response size (bytes) and series count
curl -s http://10.2.9.14:9102/metrics | wc -c
curl -s http://10.2.9.14:9102/metrics | grep -vcE '^(#|$)'
# Biggest metric families in the payload
curl -s http://10.2.9.14:9102/metrics \
| grep -vE '^#' | awk '{print $1}' | sed 's/{.*//' | sort | uniq -c | sort -rn | head
# Configured body_size_limit for the job
curl -s http://localhost:9090/api/v1/status/config | jq -r '.data.yaml' \
| grep -A6 'job_name: app-metrics' | grep body_size_limit
# Validate + reload after trimming or raising the limit
promtool check config /etc/prometheus/prometheus.yml \
&& curl -s -XPOST http://localhost:9090/-/reload
Conclusion
body size limit exceeded means a target’s raw /metrics payload outgrew the byte cap you set, and Prometheus dropped the whole scrape to protect itself. The right response is diagnostic, not reflexive: measure the real response size, rank the fattest metric families, and decide whether the growth is legitimate or a cardinality bug. When a label is exploding, fix it with metric_relabel_configs at the source so the cap can stay as a tripwire; only raise body_size_limit when the volume is genuinely justified and sized with headroom. Paired with sample_limit, a byte cap keeps a single runaway exporter from quietly filling your TSDB — as long as you treat the error as an early warning rather than a nuisance to silence.
Fixed it? Get 500 Prometheus & Monitoring & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.