Prometheus Error Guide: 'invalid metric type' Scrape Parse Failure
Fix Prometheus 'invalid metric type' scrape parse errors: correct misspelled # TYPE tokens, serve OpenMetrics types with the right content-type, and validate with promtool.
- #prometheus-monitoring
- #troubleshooting
- #errors
- #scrape
Exact Error Message
invalid metric type is a parse-time failure on the scrape path. Prometheus fetches the /metrics body, hands it to the exposition parser, and the parser chokes on a # TYPE line whose type token is not a recognised metric type. It shows up in the Prometheus log:
ts=2026-06-27T09:12:44.501Z caller=scrape.go:1654 level=warn component="scrape manager" scrape_pool=app msg="Append failed" err="invalid metric type \"guage\""
On the Targets page (/targets) the same target’s last error reads:
DOWN http://app-7d9c:9100/metrics "invalid metric type \"summery\""
The detail that trips people up: because the parser rejects the body as it reads it, the whole scrape is thrown away. No samples from that target are stored for that scrape. Depending on where the bad line sits and your Prometheus version, up may briefly still report 1 while the real metrics quietly vanish, or the target flips to DOWN with the error above. Either way, the data disappears.
What the Error Means
Prometheus exposition (and OpenMetrics) format declares a metric’s type with a # TYPE comment line before the samples:
# HELP app_requests_total Total requests
# TYPE app_requests_total guage
app_requests_total 42
That guage is the problem — it’s a typo for gauge. The parser only accepts a fixed set of type tokens. When the token isn’t one of them, it raises invalid metric type "<token>" and aborts.
The valid types are:
- Plain Prometheus text format:
counter,gauge,histogram,summary,untyped. - OpenMetrics adds:
unknown(the OpenMetrics name for untyped),info,stateset,gaugehistogram.
So invalid metric type means the token after the metric name on a # TYPE line is not one of those. In practice it is almost always a misspelling (guage, summery, histo, count), a hand-rolled exporter writing the line by hand, a string-templating bug, or an OpenMetrics-only type (info, stateset, gaugehistogram) being served in plain text format where the parser does not recognise it.
Common Causes
- Hand-rolled exporter emitting bad TYPE lines. A custom exporter that builds the exposition body with string concatenation rather than a client library, and gets a token wrong.
- A typo in a literal.
guageinstead ofgauge,summeryinstead ofsummary,histogrammistyped ashistgram. The classics. - A string-formatting / templating bug. A template that interpolates the type from a config field, where the field is blank or holds the wrong value, producing
# TYPE app_xor# TYPE app_x %s. - OpenMetrics types without the OpenMetrics content-type. Serving
info,stateset, orgaugehistogramwhile responding withContent-Type: text/plain. Prometheus uses the plain-text parser, which rejects those tokens. - A proxy or transform mangling the body. A sidecar, relabeling proxy, or gzip/charset rewrite that corrupts or re-wraps
# TYPElines. - Double TYPE lines. Two
# TYPEdeclarations for the same metric, or a# TYPEline emitted after samples have already started — a structural violation that surfaces as a parse error. - The type token in the wrong position.
# TYPE counter app_requests_total(type and name swapped) — the parser readscounteras the metric name andapp_requests_totalas the type, which is invalid.
How to Reproduce the Error
Stand up a one-line “exporter” that serves a deliberately misspelled type, then point a scrape job at it:
# Serve a /metrics body with a bad TYPE token on port 9100
mkdir -p /tmp/badexp && cat > /tmp/badexp/metrics <<'EOF'
# HELP app_requests_total Total requests
# TYPE app_requests_total guage
app_requests_total 42
EOF
( cd /tmp/badexp && python3 -m http.server 9100 ) &
Add a scrape job for localhost:9100, reload Prometheus, and watch the log:
journalctl -u prometheus -f | grep -i 'invalid metric type'
level=warn component="scrape manager" scrape_pool=app msg="Append failed" err="invalid metric type \"guage\""
To reproduce the OpenMetrics case, serve a body containing # TYPE app_build info over Content-Type: text/plain: Prometheus uses the text parser and rejects info as an invalid type.
Diagnostic Commands
All of these are read-only — they fetch and inspect the exposition body without changing anything.
Validate the whole /metrics body against the exposition spec. promtool check metrics flags malformed lines and bad type tokens directly:
curl -s http://target:9100/metrics | promtool check metrics
app_requests_total: invalid metric type "guage"
List every # TYPE line with its line number so you can eyeball the tokens:
curl -s http://target:9100/metrics | grep -n '# TYPE'
17:# TYPE app_requests_total guage
42:# TYPE app_latency_seconds summery
Check the content-type the target advertises — this decides whether the OpenMetrics or plain-text parser runs:
curl -sI http://target:9100/metrics | grep -i content-type
content-type: text/plain; version=0.0.4
Pull the offending target straight off the API, including its last scrape error:
curl -s http://localhost:9090/api/v1/targets \
| jq '.data.activeTargets[] | select(.health=="down") | {scrapeUrl, lastError}'
{ "scrapeUrl": "http://app-7d9c:9100/metrics", "lastError": "invalid metric type \"summery\"" }
Step-by-Step Resolution
1. Find the offending target. Use the /api/v1/targets query above to get the scrapeUrl and the exact lastError token (e.g. "guage", "summery", "info"). That token is the literal you’re hunting.
2. Find the offending TYPE line. Curl the target’s /metrics and grep for the type lines:
curl -s http://app-7d9c:9100/metrics | grep -n '# TYPE'
Match the bad token from step 1 to its line. Confirm with promtool check metrics, which names the metric and the invalid type.
3. Fix the TYPE token to a valid type. In the exporter, change the literal to a real type. If you control the code, prefer the official client library (Go prometheus, prometheus_client for Python, etc.) — it writes correct # TYPE lines for you and makes typos impossible:
# TYPE app_requests_total counter
app_requests_total 42
(Also fix the semantics: a monotonically increasing total like app_requests_total should be a counter, not a gauge.)
4. If the token is an OpenMetrics-only type, serve the right content-type. Tokens info, stateset, and gaugehistogram are valid only under the OpenMetrics parser. Make the exporter respond with the OpenMetrics content type so Prometheus selects that parser:
Content-Type: application/openmetrics-text; version=1.0.0; charset=utf-8
OpenMetrics bodies must also end with # EOF. With the correct content-type, those tokens parse cleanly.
5. Validate before re-scraping. Re-run the read-only check until it’s clean:
curl -s http://app-7d9c:9100/metrics | promtool check metrics
6. Re-scrape. Trigger a fresh scrape (wait one interval, or hit /api/v1/targets after a reload) and confirm the target is up with lastError empty. The previously missing series should reappear.
Prevention and Best Practices
- Use a client library, not string formatting. Hand-built exposition is where almost every
invalid metric typeoriginates. Client libraries emit valid# TYPElines and pick correct types. - Run
promtool check metricsin CI against any custom exporter’s sample output, so a misspelled token fails the build instead of a production scrape. - Pin the content-type deliberately. If you use OpenMetrics-only types, assert
application/openmetrics-textin a test; if you don’t, stick to the five plain-text types. - Match type to semantics. A total →
counter, a current value →gauge. Getting the type wrong doesn’t just risk a parse error; it breaks PromQL functions likerate()(see Related Errors). - Alert on scrape health. Watch
up == 0and scrape-error counters so a parse failure surfaces immediately rather than as silently missing data.
Related Errors
- PromQL parse error — a syntax failure in a query expression, not in the scraped body. Same word (“parse”), entirely different stage. See PromQL parse error.
rate/increaseon a non-counter — the metric parses fine but its declared type doesn’t match how it’s used;rate()on agaugeproduces nonsense or warns. The fix is the same correct-the-TYPE-token discipline, applied to semantics rather than spelling.- Target down /
up == 0— a parse rejection can flip a target to down; rule out connectivity first. See target down and up zero.
Frequently Asked Questions
Why did all my metrics from that target disappear, not just the bad one?
Because the parser rejects the body as a unit. When it hits an invalid # TYPE line it aborts the whole parse, so no samples from that scrape are stored — even the well-formed metrics above and below the bad line.
The error says up is still 1 but my data is gone. How?
Depending on version and where the bad line sits, the staleness/health signal can lag the data rejection. The reliable signal is the target’s lastError on /api/v1/targets and the missing series — not just up.
Is info an invalid metric type?
Only in plain Prometheus text format. info, stateset, and gaugehistogram are valid OpenMetrics types. Serve them with Content-Type: application/openmetrics-text and Prometheus parses them correctly.
Will promtool catch this before I deploy?
Yes. curl -s http://target/metrics | promtool check metrics validates the exposition and names any invalid type token. Wire it into CI against your exporter’s sample output and bad TYPE tokens never reach a live scrape.
Download the Free 500-Prompt DevOps AI Toolkit
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.