prometheus/node_exporter · error
unknown metric type
Error message
unknown metric type
What it means
convertMetricFamily in collector/textfile.go switches on the protobuf metric type of each MetricFamily parsed from a .prom textfile and panics with "unknown metric type" when the family's type is none of COUNTER, GAUGE, UNTYPED, SUMMARY, or HISTOGRAM. Since the textfile parser produces those five types plus METRIC_TYPE_UNKNOWN-equivalents, this panic fires on families whose type field is unset, invalid, or comes from a newer client_golang dto with an unrecognized type value.
Solutions
- Find the offending .prom file in the textfile collector directory (--collector.textfile.directory, default /var/lib/node_exporter/textfile_collector) and fix or remove its malformed/missing # TYPE line
- Validate the file with promtool check metrics before writing it; have writers emit only counter/gauge/untyped/summary/histogram types
- If it comes from a new client_model MetricType enum value, add a case (or skip-with-warning) for it in convertMetricFamily instead of panicking
- As a workaround, replace the metric with an equivalent untyped one (# TYPE name untyped) that the switch handles
Example fix
// before
default:
panic("unknown metric type")
// after
default:
logger.Warn("Ignoring unsupported metric type in textfile", "type", metricType, "metric", *metricFamily.Name)
continue Defensive patterns
Strategy: validation
Validate before calling
// validate a .prom file before dropping it into the textfile directory
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
p, err := expfmt.NewTextParser(0).TextToMetricFamilies(f)
if err != nil {
return err
}
for name, mf := range p {
switch mf.GetType() {
case dto.MetricType_COUNTER, dto.MetricType_GAUGE, dto.MetricType_UNTYPED,
dto.MetricType_SUMMARY, dto.MetricType_HISTOGRAM:
default:
return fmt.Errorf("metric %s has unsupported type %s", name, mf.GetType())
}
} Type guard
func isSupportedMetricType(t *dto.MetricType) bool {
switch *t {
case dto.MetricType_COUNTER, dto.MetricType_GAUGE, dto.MetricType_UNTYPED,
dto.MetricType_SUMMARY, dto.MetricType_HISTOGRAM:
return true
}
return false
} Try / catch
// The panic happens inside the collector's scrape; if embedding convertMetricFamily in a fork, recover per family:
func safeConvert(mf *dto.MetricFamily, ch chan<- prometheus.Metric, logger *slog.Logger) {
defer func() {
if r := recover(); r != nil {
logger.Warn("skipping bad textfile metric family", "family", mf.GetName(), "panic", r)
}
}()
convertMetricFamily(mf, ch, logger)
} Prevention
- Only write .prom files that pass promtool check metrics into the textfile directory
- Write files atomically (tmp file + rename) so a partially written TYPE line is never parsed
- Keep client_golang/client_model versions consistent between the metric writer and node_exporter
- Emit untyped for metrics whose kind is uncertain instead of omitting or inventing a TYPE line
When it happens
Trigger: A .prom file in the textfile collector directory contains a # TYPE line that expvar/promtext parsing maps to an unset or unknown dto.MetricType (e.g. an empty/garbage TYPE line), or the parsed dto.MetricFamily carries a type value not covered by the switch (such as MetricType_GAUGE_HISTOGRAM from a newer protobuf schema).
Common situations: Hand-written or tool-generated .prom files with malformed # TYPE comments; stale textfile files written by an older exporter version; a client_model/client_golang upgrade introducing new MetricType enum values while node_exporter's textfile switch has not been extended.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- failed to parse textfile data from
- textfile contains unsupported client-side timestamps…
- collector returned no data
- device-exclude & device-include are mutually exclusive
- getifaddrs() failed
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/c93c711f5e55811d.
Report an issue: GitHub.
Appendix: source
Thrown at collector/textfile.go:145
quantiles, values...,
)
case dto.MetricType_HISTOGRAM:
buckets := map[float64]uint64{}
for _, b := range metric.Histogram.Bucket {
buckets[b.GetUpperBound()] = b.GetCumulativeCount()
}
ch <- prometheus.MustNewConstHistogram(
prometheus.NewDesc(
*metricFamily.Name,
metricFamily.GetHelp(),
names, nil,
),
metric.Histogram.GetSampleCount(),
metric.Histogram.GetSampleSum(),
buckets, values...,
)
default:
panic("unknown metric type")
}
if metricType == dto.MetricType_GAUGE || metricType == dto.MetricType_COUNTER || metricType == dto.MetricType_UNTYPED {
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
*metricFamily.Name,
metricFamily.GetHelp(),
names, nil,
),
valType, val, values...,
)
}
}
}
func (c *textFileCollector) exportMTimes(mtimes map[string]time.Time, ch chan<- prometheus.Metric) {
if len(mtimes) == 0 {
return
}View on GitHub (pinned to 17ddd77c59)