prometheus/node_exporter · error

failed to open sysfs

Error message

failed to open sysfs: %w

What it means

NewTapestatsCollector constructs its collector by opening a sysfs filesystem handle via sysfs.NewFS(*sysPath) (default /sys). If that path cannot be opened/read as a filesystem, the collector cannot be created and construction fails with this wrapped error. This is a startup-time failure, not a scrape-time one.

Solutions

  1. Check the --path.sysfs value points at a mounted sysfs directory (default /sys)
  2. Mount sysfs if missing: on hosts it is normally there; in containers pass --path.sysfs=/host/sys with -v /sys:/host/sys:ro
  3. Test access manually: ls /sys/class/scsi_tape as the user node_exporter runs as
  4. If tapestats is not needed, disable it with --collector.tapestats to avoid constructing the collector

Example fix

// before: container without sysfs
docker run prom/node-exporter --collector.tapestats
// after
docker run -v /sys:/host/sys:ro prom/node-exporter --path.sysfs=/host/sys
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(*sysPath); err != nil || !fi.IsDir() {
    // fix --path.sysfs before enabling tapestats
}

Try / catch

coll, err := NewTapestatsCollector(logger)
if err != nil {
    logger.Warn("tapestats disabled", "err", err)
    coll = nil
}

Prevention

When it happens

Trigger: The --path.sysfs flag points at a nonexistent or unreadable directory, or sysfs.NewFS cannot stat/access the given root path at collector construction time.

Common situations: Typo in --path.sysfs when testing; running node_exporter in a minimal container where /sys is not mounted; chroot/jail environments without sysfs mounted; permission restrictions from seccomp/AppArmor blocking /sys access.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at collector/tapestats_linux.go:60

	writesCompletedTotal  *prometheus.Desc
	writeTimeSeconds      *prometheus.Desc
	residualTotal         *prometheus.Desc
	fs                    sysfs.FS
	logger                *slog.Logger
}

func init() {
	registerCollector("tapestats", defaultEnabled, NewTapestatsCollector)
}

// NewTapestatsCollector returns a new Collector exposing tape device stats.
// Docs from https://www.kernel.org/doc/html/latest/scsi/st.html#sysfs-and-statistics-for-tape-devices
func NewTapestatsCollector(logger *slog.Logger) (Collector, error) {
	var tapeLabelNames = []string{"device"}

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

	tapeSubsystem := "tape"

	return &tapestatsCollector{
		ignoredDevicesPattern: regexp.MustCompile(*ignoredTapeDevices),

		ioNow: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, tapeSubsystem, "io_now"),
			"The number of I/Os currently outstanding to this device.",
			tapeLabelNames, nil,
		),
		ioTimeSeconds: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, tapeSubsystem, "io_time_seconds_total"),
			"The amount of time spent waiting for all I/O to complete (including read and write). This includes tape movement commands such as seeking between file or set marks and implicit tape movement such as when rewind on close tape devices are used.",
			tapeLabelNames, nil,
		),
		othersCompletedTotal: prometheus.NewDesc(

View on GitHub (pinned to 17ddd77c59)