prometheus/node_exporter · error

error obtaining SCSITape class info

Error message

error obtaining SCSITape class info: %s

What it means

Update calls c.fs.SCSITapeClass() to read /sys/class/scsi_tape statistics. If the read fails with an error other than os.IsNotExist (which is gracefully handled by returning ErrNoData), the error is wrapped as this message. It means sysfs was reachable but reading the SCSI tape class directory or its per-device stat files failed.

Solutions

  1. Check dmesg for SCSI tape (st) device errors; fix underlying hardware/cable issues if I/O errors occur
  2. Verify the exporter user can read /sys/class/scsi_tape and its device stat files
  3. Re-check the --path.sysfs flag points at a real, complete sysfs tree
  4. If the error is transient (device removal race), ignore occasional scrape failures or filter the device with --collector.tapestats.ignored-devices
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.ReadDir("/sys/class/scsi_tape"); err != nil {
    // no tape devices or sysfs unreadable — disable tapestats
}

Try / catch

if err := coll.Update(ch); err != nil {
    if errors.Is(err, ErrNoData) {
        return // handled: no tape stats
    }
    log.Warn("tapestats scrape failed", "err", err)
}

Prevention

When it happens

Trigger: /sys/class/scsi_tape exists but a subdirectory or stat file cannot be read (I/O error, permission denied, race with device removal); the sysfs filesystem returned an unexpected error while enumerating tape devices.

Common situations: Faulty SCSI tape hardware generating I/O errors on stat reads; udev/sysfs races when a tape device is hot-unplugged mid-scrape; SELinux/AppArmor denying reads of scsi_tape attributes; broken --path.sysfs pointing to a partial copy of sysfs.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at collector/tapestats_linux.go:130

		),
		residualTotal: prometheus.NewDesc(
			prometheus.BuildFQName(namespace, tapeSubsystem, "residual_total"),
			"The number of times during a read or write we found the residual amount to be non-zero. This should mean that a program is issuing a read larger thean the block size on tape. For write not all data made it to tape.",
			tapeLabelNames, nil,
		),
		logger: logger,
		fs:     fs,
	}, nil
}

func (c *tapestatsCollector) Update(ch chan<- prometheus.Metric) error {
	tapes, err := c.fs.SCSITapeClass()
	if err != nil {
		if os.IsNotExist(err) {
			c.logger.Debug("scsi_tape stats not found, skipping")
			return ErrNoData
		}
		return fmt.Errorf("error obtaining SCSITape class info: %s", err)
	}

	for _, tape := range tapes {
		if c.ignoredDevicesPattern.MatchString(tape.Name) {
			c.logger.Debug("Ignoring device", "device", tape.Name)
			continue
		}
		ch <- prometheus.MustNewConstMetric(c.ioNow, prometheus.GaugeValue, float64(tape.Counters.InFlight), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.ioTimeSeconds, prometheus.CounterValue, float64(tape.Counters.IoNs)*0.000000001, tape.Name)
		ch <- prometheus.MustNewConstMetric(c.othersCompletedTotal, prometheus.CounterValue, float64(tape.Counters.OtherCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.readByteTotal, prometheus.CounterValue, float64(tape.Counters.ReadByteCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.readsCompletedTotal, prometheus.CounterValue, float64(tape.Counters.ReadCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.readTimeSeconds, prometheus.CounterValue, float64(tape.Counters.ReadNs)*0.000000001, tape.Name)
		ch <- prometheus.MustNewConstMetric(c.residualTotal, prometheus.CounterValue, float64(tape.Counters.ResidCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.writtenByteTotal, prometheus.CounterValue, float64(tape.Counters.WriteByteCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.writesCompletedTotal, prometheus.CounterValue, float64(tape.Counters.WriteCnt), tape.Name)
		ch <- prometheus.MustNewConstMetric(c.writeTimeSeconds, prometheus.CounterValue, float64(tape.Counters.WriteNs)*0.000000001, tape.Name)
	}

View on GitHub (pinned to 17ddd77c59)