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

  1. 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
  2. Validate the file with promtool check metrics before writing it; have writers emit only counter/gauge/untyped/summary/histogram types
  3. 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
  4. 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

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


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)