prometheus/node_exporter · error

failed to open sysfs

Error message

failed to open sysfs: %w

What it means

NewCPUCollector opens a sysfs handle at --path.sysfs (needed e.g. for CPU frequency and isolated-CPU info) and returns this wrapped error if that filesystem cannot be opened. Like the procfs failure it aborts collector construction at startup.

Solutions

  1. Mount /sys (read-only) into the container or set --path.sysfs to the correct location
  2. Verify the target directory is a real sysfs mount and readable
  3. Run the platform-correct exporter binary if not on Linux

Example fix

// before
docker run node_exporter  # no /sys mounted
// after
docker run -v /sys:/host/sys:ro node_exporter --path.sysfs=/host/sys
Defensive patterns

Strategy: validation

Validate before calling

// Go: pre-flight sysfs availability
if fi, err := os.Stat(filepath.Join(*sysPath, "devices/system/cpu")); err != nil || !fi.IsDir() {
    return fmt.Errorf("--path.sysfs %q lacks /sys/devices/system/cpu", *sysPath)
}

Try / catch

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

Prevention

When it happens

Trigger: sysfs.NewFS(*sysPath) errors: --path.sysfs does not exist, is not sysfs, or is unreadable.

Common situations: Containers that do not mount /sys (or mount it elsewhere, e.g. /host/sys) without adjusting --path.sysfs; running the Linux exporter build outside Linux; restrictive mount namespaces.

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

Appendix: source

Thrown at collector/cpu_linux.go:82

	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,
		cpu:    nodeCPUSecondsDesc,
		cpuInfo: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "info"),
			"CPU information from /proc/cpuinfo.",
			[]string{"package", "core", "cpu", "vendor", "family", "model", "model_name", "microcode", "stepping", "cachesize"}, nil,

View on GitHub (pinned to 17ddd77c59)