prometheus/node_exporter · error
device node string didn't match regexp
Error message
device node string didn't match regexp: %s
What it means
getMemInfoNuma extracts the NUMA node number from each globbed path with the regexp .*devices/system/node/node([0-9]*). If a path returned by the glob does not match (no capture group), the collector aborts with this error rather than emit metrics with an unknown node label. This indicates the sysfs topology contains node entries outside the expected nodeN naming scheme.
Solutions
- Inspect /sys/devices/system/node (or --path.sysfs/devices/system/node) and identify the entry that fails to match node[0-9]+; remove or correct the offending path if it is a fixture/mirror
- Verify --path.sysfs points at a genuine sysfs mount, not a partial copy with stray entries
- If the kernel exposes valid nodes with nonstandard names, update the procfs/sysfs source or the regexp in a patched build to match the actual naming scheme
- As a workaround, disable the meminfo_numa collector (--collector.meminfo_numa is disabled by default) since plain meminfo collection is unaffected
Example fix
// before var meminfoNodeRE = regexp.MustCompile(`.*devices/system/node/node([0-9]*)`) // after (require at least one digit so 'node' with no number is rejected by the glob instead) var meminfoNodeRE = regexp.MustCompile(`.*devices/system/node/node([0-9]+)$`)
Defensive patterns
Strategy: validation
Validate before calling
re := regexp.MustCompile(`.*devices/system/node/node([0-9]+)$`)
nodes, _ := filepath.Glob("/sys/devices/system/node/node[0-9]*")
for _, node := range nodes {
if !re.MatchString(node) {
log.Printf("unexpected sysfs entry, meminfo_numa would fail: %s", node)
}
} Try / catch
// node_exporter treats any Update error as a failed scrape for this collector;
// guard by pre-validating sysfs before enabling the collector flag:
if bad := nonstandardNodePaths("/sys/devices/system/node"); len(bad) > 0 {
log.Printf("not enabling meminfo_numa, unexpected entries: %v", bad)
} else {
flags = append(flags, "--collector.meminfo_numa")
} Prevention
- Point --path.sysfs only at genuine sysfs mounts, not fixture/mirror directories
- Audit /sys/devices/system/node for nonstandard entries in virtualized environments
- Keep node_exporter updated if your kernel exposes nonstandard NUMA naming
When it happens
Trigger: filepath.Glob(sysFilePath("devices/system/node/node[0-9]*")) returns a path whose tail after 'node' is empty or non-numeric — e.g. a directory named exactly 'node' with no digits, or symlinks/odd entries under /sys/devices/system/node that satisfy the glob but not the capture group.
Common situations: Exotic or virtualized environments presenting nonstandard sysfs NUMA entries; overlaid or synthetic sysfs in containers/sandboxes; custom --path.sysfs pointing at a fixture or mirror directory that contains files not matching nodeN; kernel/firmware quirks exposing node0 with unusual topology naming.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- couldn't get NUMA meminfo
- invalid value in meminfo
- invalid line in meminfo
- line scan did not return 2 fields
- invalid value in numastat
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/4e7cdf174a595ec8.
Report an issue: GitHub.
Appendix: source
Thrown at collector/meminfo_numa_linux.go:111
return nil, err
}
defer meminfoFile.Close()
numaInfo, err := parseMemInfoNuma(meminfoFile)
if err != nil {
return nil, err
}
metrics = append(metrics, numaInfo...)
numastatFile, err := os.Open(filepath.Join(node, "numastat"))
if err != nil {
return nil, err
}
defer numastatFile.Close()
nodeNumber := meminfoNodeRE.FindStringSubmatch(node)
if nodeNumber == nil {
return nil, fmt.Errorf("device node string didn't match regexp: %s", node)
}
numaStat, err := parseMemInfoNumaStat(numastatFile, nodeNumber[1])
if err != nil {
return nil, err
}
metrics = append(metrics, numaStat...)
}
return metrics, nil
}
func parseMemInfoNuma(r io.Reader) ([]meminfoMetric, error) {
var (
memInfo []meminfoMetric
scanner = bufio.NewScanner(r)
re = regexp.MustCompile(`\((.*)\)`)
)View on GitHub (pinned to 17ddd77c59)