prometheus/node_exporter · error

couldn't get ce_count for controller/csrow

Error message

couldn't get ce_count for controller/csrow %s/%s: %w

What it means

After matching a csrow path, the EDAC collector reads <csrow>/ce_count with readUintFromFile and wraps any failure with this message, naming the controller and csrow numbers. The wrapped error carries the underlying reason (missing file, permission, non-numeric content). A failed read aborts the whole scrape of the EDAC collector.

Solutions

  1. Verify the file exists: `cat /sys/devices/system/edac/mc/mc0/csrow0/ce_count`.
  2. Check permissions/ownership of the sysfs csrow directories for the node_exporter user.
  3. Ensure the correct EDAC kernel module is loaded for your memory controller (e.g. amd64_edac, i7core_edac) so all standard attributes are exposed.
  4. Check for mid-scrape module unload races; pin module loading via modprobe config.

Example fix

// before: any read error aborts the collector
if err != nil {
	return fmt.Errorf("couldn't get ce_count for controller/csrow %s/%s: %w", controllerNumber, csrowNumber, err)
}

// after (caller-side guard): pre-check the attribute
if _, err := os.Stat(csrowPath + "/ce_count"); os.IsNotExist(err) {
	// disable edac collector or expect partial data
}
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require('fs');
for (const d of fs.existsSync('/sys/devices/system/edac/mc') ? fs.readdirSync('/sys/devices/system/edac/mc') : []) {
  for (const c of fs.readdirSync(`/sys/devices/system/edac/mc/${d}`).filter(x => x.startsWith('csrow'))) {
    const f = `/sys/devices/system/edac/mc/${d}/${c}/ce_count`;
    if (!fs.existsSync(f) || isNaN(parseInt(fs.readFileSync(f, 'utf8'), 10))) {
      console.warn(`edac ce_count unreadable: ${f} — disable --collector.edac`);
    }
  }
}

Try / catch

try {
  await scrapeNodeExporter();
} catch (e) {
  if (String(e).includes('couldn\'t get ce_count')) {
    console.warn('EDAC ce_count unreadable; disabling edac collector: --collector.edac=false');
  } else throw e;
}

Prevention

When it happens

Trigger: readUintFromFile(filepath.Join(csrow, "ce_count")) fails during Update: file absent, unreadable, or containing a non-integer value.

Common situations: Kernel EDAC driver exposing csrow dirs without ce_count (some drivers omit it); restrictive permissions after hardening; proc/sys fs mounted oddly in containers; transient removal of the csrow dir on module unload mid-scrape.

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

Appendix: source

Thrown at collector/edac_linux.go:154

		}
		ch <- prometheus.MustNewConstMetric(
			edacCsRowUECount, prometheus.CounterValue, float64(value), controllerNumber, "unknown")

		// For each controller, walk the csrow directories.
		csrows, err := filepath.Glob(controller + "/csrow[0-9]*")
		if err != nil {
			return err
		}
		for _, csrow := range csrows {
			csrowMatch := edacMemCsrowRE.FindStringSubmatch(csrow)
			if csrowMatch == nil {
				return fmt.Errorf("csrow string didn't match regexp: %s", csrow)
			}
			csrowNumber := csrowMatch[1]

			value, err = readUintFromFile(filepath.Join(csrow, "ce_count"))
			if err != nil {
				return fmt.Errorf("couldn't get ce_count for controller/csrow %s/%s: %w", controllerNumber, csrowNumber, err)
			}
			ch <- prometheus.MustNewConstMetric(
				edacCsRowCECount, prometheus.CounterValue, float64(value), controllerNumber, csrowNumber)

			value, err = readUintFromFile(filepath.Join(csrow, "ue_count"))
			if err != nil {
				return fmt.Errorf("couldn't get ue_count for controller/csrow %s/%s: %w", controllerNumber, csrowNumber, err)
			}
			ch <- prometheus.MustNewConstMetric(
				edacCsRowUECount, prometheus.CounterValue, float64(value), controllerNumber, csrowNumber)

			channelFiles, err := filepath.Glob(csrow + "/ch*_ce_count")
			if err != nil {
				return err
			}
			for _, chFile := range channelFiles {
				match := edacMemChannelRE.FindStringSubmatch(filepath.Base(chFile))
				if match == nil {

View on GitHub (pinned to 17ddd77c59)