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
- Point --path.sysfs at a valid sysfs mount (default /proc/sys... default is /sys): --path.sysfs=/sys.
- In containers, mount the host sysfs read-only: -v /sys:/host/sys:ro and use --path.sysfs=/host/sys.
- Verify the NVMe class dir exists: 'ls /sys/class/nvme'.
- 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
- Mount host /sys read-only in containers and set --path.sysfs=/host/sys.
- Keep the default /sys unless you specifically remap sysfs.
- Verify /sys/class/nvme exists when enabling --collector.nvme.
- Smoke-test images with the full flag set before production rollout.
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
- failed to open sysfs
- failed to open procfs
- failed to open procfs
- failed to open procfs
- error obtaining NVMe class info
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)