prometheus/node_exporter · error

could not parse expected integer value for

Error message

could not parse expected integer value for %q: %w

What it means

parsePoolProcfsFile walks a /proc/spl/kstat/zfs/<pool> file whose first line is a header of column names. For each row it expects every value to parse as uint64; when strconv.ParseUint fails it wraps the parse error with the fully qualified kstat key (e.g. kstat.zfs.misc.zfetch_reclaim_success.hits) so the offending metric is identified. This means the fixture/proc file contained a non-numeric token where the header declared a numeric column.

Solutions

  1. Inspect the reported kstat key's column in the procfs file/fixture and fix the non-numeric value
  2. Verify the header line and data line have the same number of fields (no shift)
  3. Regenerate fixtures from a real system instead of hand-editing them
  4. Confirm the ZFS build emits uint64 values for that kstat

Example fix

// before
value, err := strconv.ParseUint(line[i], 10, 64)
// after
value, err := strconv.ParseUint(strings.TrimSpace(line[i]), 10, 64)
if err != nil {
    return fmt.Errorf("could not parse expected integer value for %q: %w", key, err)
}
Defensive patterns

Strategy: validation

Validate before calling

for i, v := range line {
    if _, err := strconv.ParseUint(strings.TrimSpace(v), 10, 64); err != nil {
        // skip or fix fixture row i before calling handler
    }
}

Try / catch

if err := c.parsePoolProcfsFile(f, pool, handler); err != nil {
    var numErr *strconv.NumError
    if errors.As(err, &numErr) {
        c.logger.Warn("skipping malformed kstat value", "err", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: updatePoolStats reads a zpool kstat procfs file and a field value on a data line is empty, contains a value like '-' or a float, or columns shifted so a non-numeric field aligns with a numeric header column.

Common situations: Corrupted or hand-edited /proc/spl/kstat/zfs fixtures in tests; running against a ZFS version that emits new non-numeric columns; misaligned columns after header/value mismatch in mock files.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at collector/zfs_linux.go:288

		}
		if !parseLine {
			continue
		}

		zpoolPathElements := strings.Split(zpoolPath, "/")
		pathLen := len(zpoolPathElements)
		if pathLen < 2 {
			return fmt.Errorf("zpool path did not return at least two elements")
		}
		zpoolName := zpoolPathElements[pathLen-2]
		zpoolFile := zpoolPathElements[pathLen-1]

		for i, field := range fields {
			key := fmt.Sprintf("kstat.zfs.misc.%s.%s", zpoolFile, field)

			value, err := strconv.ParseUint(line[i], 10, 64)
			if err != nil {
				return fmt.Errorf("could not parse expected integer value for %q: %w", key, err)
			}
			handler(zpoolName, zfsSysctl(key), value)
		}
	}

	return scanner.Err()
}

func (c *zfsCollector) parsePoolObjsetFile(reader io.Reader, zpoolPath string, handler func(string, string, zfsSysctl, uint64)) error {
	scanner := bufio.NewScanner(reader)

	parseLine := false
	var zpoolName, datasetName string
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Fields(line)

		if !parseLine && len(parts) == 3 && parts[0] == "name" && parts[1] == "type" && parts[2] == "data" {

View on GitHub (pinned to 17ddd77c59)