prometheus/node_exporter · error

couldn't get NUMA meminfo

Error message

couldn't get NUMA meminfo: %w

What it means

The meminfo_numa collector's Update wraps any failure from getMemInfoNuma in this error. getMemInfoNuma walks /sys/devices/system/node/node*/ and reads each node's meminfo and numastat files; a failure anywhere in that walk (glob error, file open error, parse error, regexp mismatch) aborts the whole NUMA collection for that scrape. It is the collector-level wrapper, so the root cause is always in the wrapped inner error.

Solutions

  1. Inspect the wrapped inner error in the log to find the specific failing file/node, then verify that node's meminfo/numastat files are readable (`cat /sys/devices/system/node/node0/meminfo`)
  2. Confirm NUMA topology is valid: check /sys/devices/system/node exists and node paths match node[0-9]+ after hotplug events
  3. Fix /sys read permissions or mount /sys read-only into containers: -v /sys:/host/sys:ro and set --path.sysfs=/host/sys
  4. If the host has no NUMA support, leave this collector disabled (it is disabled by default; enable only with --collector.meminfo_numa)

Example fix

// before (container start)
docker run --rm my-node-exporter --collector.meminfo_numa
// after
docker run --rm -v /sys:/host/sys:ro my-node-exporter --collector.meminfo_numa --path.sysfs=/host/sys
Defensive patterns

Strategy: validation

Validate before calling

const sysPath = "/sys/devices/system/node"
nodes, err := filepath.Glob(filepath.Join(sysPath, "node[0-9]*"))
if err != nil || len(nodes) == 0 {
    log.Printf("NUMA sysfs unavailable at %s; leave collector.meminfo_numa disabled", sysPath)
}

Try / catch

// Only enable the NUMA collector when the topology is present:
if _, err := os.Stat("/sys/devices/system/node"); err == nil {
    if err := registry.Register(collector); err != nil {
        log.Printf("numa collector registration failed: %v", err)
    }
}

Prevention

When it happens

Trigger: Update -> getMemInfoNuma fails because filepath.Glob on sys/devices/system/node/node[0-9]* errored, a node's meminfo or numastat file could not be opened or parsed, or a node path did not match meminfoNodeRE.

Common situations: Non-NUMA or unusual kernels where /sys/devices/system/node is absent or nodes are exposed with unexpected naming; read-restricted /sys mounts in containers; corrupted or truncated sysfs files during hotplug events (CPU/memory hotplug changing node topology mid-scrape).

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07). Data as JSON: /api/errors/38ee42751e3731b2. Report an issue: GitHub.

Appendix: source

Thrown at collector/meminfo_numa_linux.go:65

	logger      *slog.Logger
}

func init() {
	registerCollector("meminfo_numa", defaultDisabled, NewMeminfoNumaCollector)
}

// NewMeminfoNumaCollector returns a new Collector exposing memory stats.
func NewMeminfoNumaCollector(logger *slog.Logger) (Collector, error) {
	return &meminfoNumaCollector{
		metricDescs: map[string]*prometheus.Desc{},
		logger:      logger,
	}, nil
}

func (c *meminfoNumaCollector) Update(ch chan<- prometheus.Metric) error {
	metrics, err := getMemInfoNuma()
	if err != nil {
		return fmt.Errorf("couldn't get NUMA meminfo: %w", err)
	}
	for _, v := range metrics {
		desc, ok := c.metricDescs[v.metricName]
		if !ok {
			desc = prometheus.NewDesc(
				prometheus.BuildFQName(namespace, memInfoNumaSubsystem, v.metricName),
				fmt.Sprintf("Memory information field %s.", v.metricName),
				[]string{"node"}, nil)
			c.metricDescs[v.metricName] = desc
		}
		ch <- prometheus.MustNewConstMetric(desc, v.metricType, v.value, v.numaNode)
	}
	return nil
}

func getMemInfoNuma() ([]meminfoMetric, error) {
	var (
		metrics []meminfoMetric

View on GitHub (pinned to 17ddd77c59)