prometheus/node_exporter · error

failed to open sysfs

Error message

failed to open sysfs: %w

What it means

NewNVMeSubsystemCollector constructs a collector that reads NVMe-oF subsystem health from /sys/class/nvme-subsystem/ via the sysfs package. It calls sysfs.NewFS(*sysPath), where sysPath is the --path.sysfs flag, and wraps any failure in "failed to open sysfs". NewFS fails when the given path does not exist or is not usable as a filesystem root, so the collector cannot be constructed.

Solutions

  1. Mount or verify sysfs exists at the configured path (default /sys): check `ls /sys/class/nvme-subsystem` and mount -t sysfs sysfs /sys if missing
  2. Fix the --path.sysfs flag to point at a real sysfs root
  3. For tests, run `make test` (or unpack collector/fixtures/sys.ttar) so the sysfs fixtures exist
  4. Confirm NVMe-oF subsystems are present; note an empty /sys/class/nvme-subsystem is not this error (missing dir at Update time maps to ErrNoData instead)

Example fix

// before
node_exporter --path.sysfs=/host/sys   # path missing in container
// after
node_exporter --path.sysfs=/sys        # or bind-mount host sysfs: -v /sys:/host/sys:ro
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(*sysPath); err != nil { return fmt.Errorf("sysfs path %q missing: %w", *sysPath, err) }

Type guard

func sysfsAvailable(path string) bool { st, err := os.Stat(path); return err == nil && st.IsDir() }

Try / catch

c, err := collector.NewNVMeSubsystemCollector(logger)
if err != nil {
    if strings.Contains(err.Error(), "failed to open sysfs") {
        logger.Warn("sysfs unavailable, NVMe metrics disabled", "err", err)
        return nil // degrade gracefully
    }
    return err
}

Prevention

When it happens

Trigger: NewNVMeSubsystemCollector returns this when sysfs.NewFS(*sysPath) fails: --path.sysfs points to a nonexistent path, or the path exists but statfs/mount checks fail (e.g. /sys not mounted, as in containers without sysfs, or a test fixture path that is missing).

Common situations: Running node_exporter in a minimal container or chroot without /sys mounted; passing a wrong --path.sysfs value; unit tests using newTestNVMeSubsystemCollector with a fixture directory that was not unpacked (make test unpacks collector/fixtures/sys.ttar); unrelated collector code reused on non-Linux.

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

Appendix: source

Thrown at collector/nvmesubsystem_linux.go:105

	)
	nvmesubsystemPathState = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "nvmesubsystem", "path_state"),
		"Current NVMe controller path state (1 for the current state, 0 for all others).",
		[]string{"subsystem", "controller", "transport", "state"}, nil,
	)
)

type nvmeSubsystemCollector struct {
	fs     sysfs.FS
	logger *slog.Logger
}

// NewNVMeSubsystemCollector returns a new Collector exposing NVMe-oF subsystem
// path health from /sys/class/nvme-subsystem/.
func NewNVMeSubsystemCollector(logger *slog.Logger) (Collector, error) {
	fs, err := sysfs.NewFS(*sysPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open sysfs: %w", err)
	}

	return &nvmeSubsystemCollector{
		fs:     fs,
		logger: logger,
	}, nil
}

func (c *nvmeSubsystemCollector) Update(ch chan<- prometheus.Metric) error {
	subsystems, err := c.fs.NVMeSubsystemClass()
	if err != nil {
		if errors.Is(err, os.ErrNotExist) || errors.Is(err, os.ErrPermission) {
			c.logger.Debug("Could not read NVMe subsystem info", "err", err)
			return ErrNoData
		}
		return fmt.Errorf("failed to scan NVMe subsystems: %w", err)
	}

View on GitHub (pinned to 17ddd77c59)