prometheus/node_exporter · error
couldn't get netstats
Error message
couldn't get netstats: %w
What it means
The netdev collector wraps any error from getNetDevStats() — the platform-specific function that gathers per-interface RX/TX counters (from /proc/net/dev on Linux, net.Interfaces + IOKit/sysctl on darwin, etc.) — as 'couldn't get netstats'. Because this is the first step of netDevCollector.Update, the whole node_network_* metric set is dropped for that scrape when it fails.
Solutions
- Read the wrapped inner error in the log line to identify the platform-level cause.
- Verify the procfs mount path matches the --path.procfs flag (default /proc) and that /proc/net/dev is readable: cat /proc/net/dev.
- In containers, mount /proc (or use the host PID namespace pattern used by the official node-exporter manifests).
- Check hidepid mount options on /proc; node_exporter needs read access to netdev entries.
- Run the collector alone (node_exporter --collector.disable-defaults --collector.netdev) to isolate the failure.
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: netdev source must be readable
f, err := os.Open(filepath.Join(*procPath, "net", "dev"))
if err != nil {
return fmt.Errorf("netdev stats source unreadable: %w", err)
}
f.Close() Try / catch
err := collector.Update(ch)
if err != nil {
if strings.Contains(err.Error(), "couldn't get netstats") {
logger.Error("netdev scrape failed; inspect wrapped cause", "err", err)
return nil // skip scrape instead of crashing exporter
}
return err
} Prevention
- Verify --path.procfs matches the actual procfs mount
- Monitor the `node_scrape_collector_success{collector="netdev"}` metric
- Keep /proc mounted and readable (watch hidepid settings)
- Pin node_exporter versions against the kernels you run
When it happens
Trigger: getNetDevStats(&c.deviceFilter, c.logger) returns error — on Linux this means procfs open or /proc/net/dev parse failure (see procNetDevStats); on darwin a net.Interfaces() failure; the error text is always this wrapper, so look at %w for the cause.
Common situations: --path.procfs pointing at a wrong or unmounted path; container missing /proc/net/dev; /proc mounted with hidepid restrictions; on darwin, network stack initialization failures in sandboxed/test environments.
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
- couldn't get netdev labels
- failed to get IPv4 sockstat data
- failed to get IPv6 sockstat data
- couldn't get udp queued bytes
- interrupts empty
AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07).
Data as JSON: /api/errors/f494e316105070ce.
Report an issue: GitHub.
Appendix: source
Thrown at collector/netdev_common.go:112
c.metricDescsMutex.Lock()
defer c.metricDescsMutex.Unlock()
if _, ok := c.metricDescs[key]; !ok {
c.metricDescs[key] = prometheus.NewDesc(
prometheus.BuildFQName(namespace, c.subsystem, key+"_total"),
fmt.Sprintf("Network device statistic %s.", key),
labels,
nil,
)
}
return c.metricDescs[key]
}
func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error {
netDev, err := getNetDevStats(&c.deviceFilter, c.logger)
if err != nil {
return fmt.Errorf("couldn't get netstats: %w", err)
}
netDevLabels, err := getNetDevLabels()
if err != nil {
return fmt.Errorf("couldn't get netdev labels: %w", err)
}
for dev, devStats := range netDev {
if !*netdevDetailedMetrics {
legacy(devStats)
}
labels := []string{"device"}
labelValues := []string{dev}
if devLabels, exists := netDevLabels[dev]; exists {
for labelName, labelValue := range devLabels {
labels = append(labels, labelName)
labelValues = append(labelValues, labelValue)View on GitHub (pinned to 17ddd77c59)