prometheus/node_exporter · error

failed to retrieve nfs stats

Error message

failed to retrieve nfs stats: %w

What it means

The NFS collector's Update calls fs.ClientRPCStats() to read /proc/net/rpc/nfs client RPC statistics via the procfs library. When that read fails with an error other than os.ErrNotExist (the common 'no NFS client' case, which is silently skipped as ErrNoData), the collector wraps the underlying error in this message. It means the /proc file exists but could not be opened, read, or parsed.

Solutions

  1. Verify node_exporter can read the file: run 'cat /proc/net/rpc/nfs' as the same user the exporter runs as.
  2. Check container/namespace setup: mount host /proc (e.g. /host/proc via --path.procfs) so the NFS stats file is visible.
  3. Inspect the wrapped cause (%w) in the log line to determine whether it is a parse error vs an open/permission error.
  4. If the file is absent on hosts without NFS, treat this as expected behavior; the collector intentionally returns ErrNoData for os.ErrNotExist.
  5. Update procfs/node_exporter if the cause is a parse failure on a newer kernel's stats format.

Example fix

// before: collector fails every scrape on restricted /proc
// after: run exporter with host procfs and read access
//   docker run -v /proc:/host/proc:ro node-exporter --path.procfs=/host/proc
// or in systemd:
//   [Service]
//   ReadOnlyPaths=/proc/net/rpc  # ensure this is NOT blocking reads
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: check readability before enabling the collector
if f, err := os.Open(filepath.Join(*procPath, "net/rpc/nfs")); err != nil {
    log.Printf("nfs stats not readable: %v", err)
} else {
    f.Close()
}

Try / catch

// treat wrapped error: distinguish not-exist from real failures
if errors.Is(err, os.ErrNotExist) {
    return ErrNoData // expected on non-NFS hosts
}
return fmt.Errorf("failed to retrieve nfs stats: %w", err) // log cause, alert on persistence

Prevention

When it happens

Trigger: c.fs.ClientRPCStats() returns a non-ErrNotExist error: e.g. permission failure reading /proc/net/rpc/nfs, a kernel producing malformed statistics text that procfs cannot parse, or a procfs library error opening the file.

Common situations: Running node_exporter in a container where /proc/net/rpc/nfs is masked or bind-mounted read-only with restricted permissions; unusual/custom kernels emitting unexpected /proc/net/rpc/nfs formatting; hardened LSM policies (SELinux/AppArmor) blocking reads of /proc/net/rpc.

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/9c6012b0c2545fec. Report an issue: GitHub.

Appendix: source

Thrown at collector/nfs_linux.go:104

		),
		nfsProceduresDesc: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, nfsSubsystem, "requests_total"),
			"Number of NFS procedures invoked.",
			[]string{"proto", "method"},
			nil,
		),
		logger: logger,
	}, nil
}

func (c *nfsCollector) Update(ch chan<- prometheus.Metric) error {
	stats, err := c.fs.ClientRPCStats()
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			c.logger.Debug("Not collecting NFS metrics", "err", err)
			return ErrNoData
		}
		return fmt.Errorf("failed to retrieve nfs stats: %w", err)
	}

	c.updateNFSNetworkStats(ch, &stats.Network)
	c.updateNFSClientRPCStats(ch, &stats.ClientRPC)
	c.updateNFSRequestsv2Stats(ch, &stats.V2Stats)
	c.updateNFSRequestsv3Stats(ch, &stats.V3Stats)
	c.updateNFSRequestsv4Stats(ch, &stats.ClientV4Stats)

	return nil
}

// updateNFSNetworkStats collects statistics for network packets/connections.
func (c *nfsCollector) updateNFSNetworkStats(ch chan<- prometheus.Metric, s *nfs.Network) {
	ch <- prometheus.MustNewConstMetric(c.nfsNetReadsDesc, prometheus.CounterValue,
		float64(s.UDPCount), "udp")
	ch <- prometheus.MustNewConstMetric(c.nfsNetReadsDesc, prometheus.CounterValue,
		float64(s.TCPCount), "tcp")
	ch <- prometheus.MustNewConstMetric(c.nfsNetConnectionsDesc, prometheus.CounterValue,

View on GitHub (pinned to 17ddd77c59)