prometheus/node_exporter · error

failed to open procfs

Error message

failed to open procfs: %w

What it means

NewCPUCollector initializes a procfs handle at --path.procfs to read /proc statistics and returns this wrapped error when the filesystem cannot be opened. Because it happens in the constructor, the cpu collector fails to register at startup rather than failing later at scrape time.

Solutions

  1. Set --path.procfs to the actual proc mount (e.g. /host/proc in containers)
  2. Confirm /proc is mounted and readable by the exporter user
  3. On non-Linux platforms use the platform-appropriate build instead

Example fix

// before
pfs, err := procfs.NewFS(*procPath) // procPath=/proc absent
// after
node_exporter --path.procfs=/host/proc
Defensive patterns

Strategy: validation

Validate before calling

// Go: ensure proc path is mounted and readable before startup
if _, err := os.Stat(filepath.Join(*procPath, "stat")); err != nil {
    log.Fatal(fmt.Sprintf("--path.procfs %s unusable: %v", *procPath, err))
}

Try / catch

if _, err := NewCPUCollector(logger); err != nil {
    if strings.Contains(err.Error(), "failed to open procfs") {
        return fmt.Errorf("fix --path.procfs (default /proc): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: procfs.NewFS(*procPath) errors during node_exporter startup: the path does not exist, is not a proc filesystem, or is unreadable.

Common situations: Incorrect --path.procfs in containers (host proc mounted at /host/proc); running the Linux build on a system without /proc; chroot/test environments lacking a proc mount.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at collector/cpu_linux.go:77

const jumpBackSeconds = 3.0

var (
	enableCPUGuest       = kingpin.Flag("collector.cpu.guest", "Enables metric node_cpu_guest_seconds_total").Default("true").Bool()
	enableCPUInfo        = kingpin.Flag("collector.cpu.info", "Enables metric cpu_info").Bool()
	flagsInclude         = kingpin.Flag("collector.cpu.info.flags-include", "Filter the `flags` field in cpuInfo with a value that must be a regular expression").String()
	bugsInclude          = kingpin.Flag("collector.cpu.info.bugs-include", "Filter the `bugs` field in cpuInfo with a value that must be a regular expression").String()
	jumpBackDebugMessage = fmt.Sprintf("CPU Idle counter jumped backwards more than %f seconds, possible hotplug event, resetting CPU stats", jumpBackSeconds)
)

func init() {
	registerCollector("cpu", defaultEnabled, NewCPUCollector)
}

// NewCPUCollector returns a new Collector exposing kernel/system statistics.
func NewCPUCollector(logger *slog.Logger) (Collector, error) {
	pfs, err := procfs.NewFS(*procPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open procfs: %w", err)
	}

	sfs, err := sysfs.NewFS(*sysPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open sysfs: %w", err)
	}

	isolcpus, err := sfs.IsolatedCPUs()
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, fmt.Errorf("unable to get isolated cpus: %w", err)
		}
		logger.Debug("couldn't open isolated file", "error", err)
	}

	c := &cpuCollector{
		procfs: pfs,
		sysfs:  sfs,

View on GitHub (pinned to 17ddd77c59)