prometheus/node_exporter · error

error obtaining PCI device info

Error message

error obtaining PCI device info: %w

What it means

Update enumerates PCI devices with c.fs.PciDevices(). ErrNotExist is downgraded to a debug log + ErrNoData, but any other error is wrapped as "error obtaining PCI device info" and fails the scrape. This signals an unexpected failure while reading PCI device attributes from sysfs, distinct from simply having no PCI info available.

Solutions

  1. Inspect the wrapped cause (%w) in the scrape error to identify the failing file/syscall
  2. Re-scrape after fixing underlying I/O problems; check dmesg for PCI hardware errors
  3. Ensure the process has read access to /sys/bus/pci/devices (avoid over-aggressive LSM/masked paths)
  4. Fix or regenerate test fixtures if running against a truncated sysfs fixture root
  5. If PCI info is genuinely absent, verify why ErrNotExist path isn't taken (path exists but is unreadable)
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(filepath.Join(*sysPath, "bus", "pci", "devices")); err != nil || !fi.IsDir() { log.Println("pci devices dir unreadable") }

Type guard

func pciDevicesReadable(sysPath string) bool {
    f, err := os.Open(filepath.Join(sysPath, "bus", "pci", "devices"))
    if err != nil { return false }
    f.Close()
    return true
}

Try / catch

if err := c.Update(ch); err != nil {
    if strings.Contains(err.Error(), "error obtaining PCI device info") {
        logger.Warn("PCI scrape failed; skipping this cycle", "err", err)
        return nil // next scrape may succeed
    }
    return err
}

Prevention

When it happens

Trigger: c.fs.PciDevices() returns a non-ErrNotExist error: permission/IO failures reading /sys/bus/pci/devices entries, readlink or attribute-read errors (e.g. EIO, EACCES variants not matching the check), malformed device directories in a test fixture root.

Common situations: Failing PCI hardware producing EIO during attribute reads; containers where most of sysfs is masked and reads return unusual errors; partially-populated test fixtures; kernel changes altering expected sysfs layout handled upstream in procfs/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/477cc391e6be5a12. Report an issue: GitHub.

Appendix: source

Thrown at collector/pcidevice_linux.go:206

			prometheus.BuildFQName(namespace, pcideviceSubsystem, "info"),
			"Non-numeric data from /sys/bus/pci/devices/<location>, value is always 1.",
			labelNames,
			nil,
		),
		valueType: prometheus.GaugeValue,
	}

	return c, nil
}

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

	for _, device := range devices {
		// The device location is represented in separated format.
		values := device.Location.Strings()
		if device.ParentLocation != nil {
			values = append(values, device.ParentLocation.Strings()...)
		} else {
			values = append(values, []string{"*", "*", "*", "*"}...)
		}

		// Add basic device information
		classID := fmt.Sprintf("0x%06x", device.Class)
		vendorID := fmt.Sprintf("0x%04x", device.Vendor)
		deviceID := fmt.Sprintf("0x%04x", device.Device)
		subsysVendorID := fmt.Sprintf("0x%04x", device.SubsystemVendor)
		subsysDeviceID := fmt.Sprintf("0x%04x", device.SubsystemDevice)

View on GitHub (pinned to 17ddd77c59)