prometheus/node_exporter · error
invalid value in meminfo
Error message
invalid value in meminfo: %w
What it means
This error is returned by parseMemInfoNuma in collector/meminfo_numa_linux.go:140 when strconv.ParseFloat fails to parse field index 3 of a line from a per-NUMA-node /sys/devices/system/node/nodeN/meminfo file. The collector expects each line to contain a numeric value at parts[3] (the byte count); any non-numeric token there aborts the whole NUMA meminfo parse. It wraps the underlying strconv error via %w.
Solutions
- Inspect the failing /sys/devices/system/node/node*/meminfo content and check which line has a non-numeric 4th field
- Verify kernel version; if the sysfs format changed, update the parser to match the new layout
- Check for truncated reads or corrupted sysfs (virtualization/container artifacts) and re-read the file
- Rebuild with -tags nomeminfo_numa to disable the meminfo_numa collector if the platform cannot be supported
- File an issue upstream with the offending line if a supported kernel produces this format
Example fix
// before
fv, err := strconv.ParseFloat(parts[3], 64)
if err != nil {
return nil, fmt.Errorf("invalid value in meminfo: %w", err)
}
// after (skip unparseable lines instead of failing the whole node)
fv, err := strconv.ParseFloat(parts[3], 64)
if err != nil {
return nil, fmt.Errorf("invalid value in meminfo: %w", err) // keep abort, but log line context
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate a meminfo line before relying on parts[3]
parts := strings.Fields(line)
if len(parts) < 4 {
return fmt.Errorf("line too short: %q", line)
}
if _, err := strconv.ParseFloat(parts[3], 64); err != nil {
return fmt.Errorf("non-numeric value in %q: %v", line, err)
} Try / catch
metrics, err := parseMemInfoNuma(f)
if err != nil {
var perr *strconv.NumError
if errors.As(err, &perr) {
// handle malformed numeric field: log and skip node or fall back to non-NUMA meminfo
}
return err
} Prevention
- Run only on kernels whose per-node meminfo matches the expected 4/5-field format
- Test the collector against your exact kernel version before deploying
- Use the nomeminfo_numa build tag on platforms with nonstandard sysfs
- Watch scrape errors on /metrics to catch format drift early
When it happens
Trigger: parseMemInfoNuma reads a line from a node's meminfo whose 4th whitespace-separated field (parts[3]) is not a valid base-10 float, e.g. 'Node 0 MemUsed: abc kB' or a truncated/garbled line missing fields so parts[3] is actually a unit or label.
Common situations: Running a nonstandard or patched kernel that formats node meminfo differently; reading a fixture/test file with malformed lines; virtualized or container environments exposing a synthesized sysfs with unexpected content; kernel version changes altering the per-node meminfo layout.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid line in meminfo
- line scan did not return 2 fields
- invalid value in numastat
- couldn't get NUMA meminfo
- device node string didn't match regexp
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/707347e1702b0ab2.
Report an issue: GitHub.
Appendix: source
Thrown at collector/meminfo_numa_linux.go:140
}
func parseMemInfoNuma(r io.Reader) ([]meminfoMetric, error) {
var (
memInfo []meminfoMetric
scanner = bufio.NewScanner(r)
re = regexp.MustCompile(`\((.*)\)`)
)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
parts := strings.Fields(line)
fv, err := strconv.ParseFloat(parts[3], 64)
if err != nil {
return nil, fmt.Errorf("invalid value in meminfo: %w", err)
}
switch l := len(parts); {
case l == 4: // no unit
case l == 5 && parts[4] == "kB": // has unit
fv *= 1024
default:
return nil, fmt.Errorf("invalid line in meminfo: %s", line)
}
metric := strings.TrimRight(parts[2], ":")
// Active(anon) -> Active_anon
metric = re.ReplaceAllString(metric, "_${1}")
memInfo = append(memInfo, meminfoMetric{metric, prometheus.GaugeValue, parts[1], fv})
}
return memInfo, scanner.Err()
}
View on GitHub (pinned to 17ddd77c59)