prometheus/node_exporter · error

failed to open sysfs

Error message

failed to open sysfs: %w

What it means

NewNVMeCollector constructs the collector by calling sysfs.NewFS(*sysPath), which validates the sysfs mount point. If the path cannot be opened (missing directory, wrong --path.sysfs value, or not a valid sysfs), the constructor returns this wrapped error and node_exporter fails to start that collector. It is a startup-time configuration error, not a scrape error.

Solutions

  1. Point --path.sysfs at a valid sysfs mount (default /proc/sys... default is /sys): --path.sysfs=/sys.
  2. In containers, mount the host sysfs read-only: -v /sys:/host/sys:ro and use --path.sysfs=/host/sys.
  3. Verify the NVMe class dir exists: 'ls /sys/class/nvme'.
  4. Check the wrapped cause (%w) to distinguish ENOENT from permission issues.

Example fix

// before: sysfs not mounted in container
//   docker run node-exporter --collector.nvme
// after
//   docker run -v /sys:/host/sys:ro node-exporter \
//     --path.sysfs=/host/sys --collector.nvme
Defensive patterns

Strategy: validation

Validate before calling

// Go: validate sysfs path before constructing the collector
if st, err := os.Stat(filepath.Join(*sysPath, "class")); err != nil || !st.IsDir() {
    log.Fatalf("invalid --path.sysfs (%s): %v", *sysPath, err)
}

Try / catch

// constructor error is fatal; wrap and surface the cause
fs, err := sysfs.NewFS(*sysPath)
if err != nil {
    return nil, fmt.Errorf("failed to open sysfs: %w", err)
}

Prevention

When it happens

Trigger: Calling NewNVMeCollector when --path.sysfs points to a nonexistent or invalid directory, causing sysfs.NewFS to fail; also triggered when the sysfs mount is absent entirely (e.g. minimal containers, non-Linux builds are gated separately).

Common situations: Running node_exporter in a container without mounting the host's /sys and without --path.sysfs=/host/sys; typos in --path.sysfs; overriding sysPath for tests to a temp dir that was cleaned up.

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

Appendix: source

Thrown at collector/nvme_linux.go:79

	nvmeNamespaceUsedBytes = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "nvme", "namespace_used_bytes"),
		"Used space of the NVMe namespace in bytes. Available in /sys/class/nvme/<device>/<namespace>/nuse",
		[]string{"device", "nsid"}, nil,
	)

	nvmeNamespaceLogicalBlockSizeBytes = prometheus.NewDesc(
		prometheus.BuildFQName(namespace, "nvme", "namespace_logical_block_size_bytes"),
		"Logical block size of the NVMe namespace in bytes. Usually 4Kb. Available in /sys/class/nvme/<device>/<namespace>/queue/logical_block_size",
		[]string{"device", "nsid"}, nil,
	)
)

// NewNVMeCollector returns a new Collector exposing NVMe stats.
func NewNVMeCollector(logger *slog.Logger) (Collector, error) {
	fs, err := sysfs.NewFS(*sysPath)
	if err != nil {
		return nil, fmt.Errorf("failed to open sysfs: %w", err)
	}
	return &nvmeCollector{
		fs:     fs,
		logger: logger,
	}, nil
}

func (c *nvmeCollector) Update(ch chan<- prometheus.Metric) error {
	devices, err := c.fs.NVMeClass()
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			c.logger.Debug("nvme statistics not found, skipping")
			return ErrNoData
		}
		return fmt.Errorf("error obtaining NVMe class info: %w", err)
	}

	for _, device := range devices {

View on GitHub (pinned to 17ddd77c59)