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

  1. 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
  2. Verify --path.sysfs points at a genuine sysfs mount, not a partial copy with stray entries
  3. 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
  4. 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

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


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)