wavetermdev/waveterm · error

procinfo: malformed stat for pid %d

Error message

procinfo: malformed stat for pid %d

What it means

The library parsed /proc/<pid>/stat and could not locate a valid comm field: there must be text of the form 'pid (comm) rest...'. It finds the first '(' and last ')'; if either is missing or the ')' does not come after '(', the stat content is considered malformed. This indicates the file did not contain an expected procfs stat record.

Source

Thrown at pkg/util/procinfo/procinfo_linux.go:68

// The comm field (field 2) is enclosed in parentheses and may contain spaces
// and even parentheses itself, so we locate the last ')' to find the field
// boundary rather than splitting on whitespace naively.
func readStat(pid int32) (*ProcInfo, error) {
	path := fmt.Sprintf("/proc/%d/stat", pid)
	data, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
			return nil, ErrNotFound
		}
		return nil, fmt.Errorf("procinfo: read %s: %w", path, err)
	}
	s := strings.TrimRight(string(data), "\n")

	// Locate comm: everything between first '(' and last ')'.
	lp := strings.Index(s, "(")
	rp := strings.LastIndex(s, ")")
	if lp < 0 || rp < 0 || rp <= lp {
		return nil, fmt.Errorf("procinfo: malformed stat for pid %d", pid)
	}

	pidStr := strings.TrimSpace(s[:lp])
	comm := s[lp+1 : rp]
	rest := strings.Fields(s[rp+1:])

	// rest[0] = field 3 (state), rest[1] = field 4 (ppid), ...
	// Fields after comm are numbered starting at 3, so rest[i] = field (i+3).
	// We need:
	//   rest[0]  = field  3  state
	//   rest[1]  = field  4  ppid
	//   rest[11] = field 14  utime
	//   rest[12] = field 15  stime
	//   rest[17] = field 20  num_threads
	//   rest[21] = field 24  rss (pages)
	if len(rest) < 22 {
		return nil, fmt.Errorf("procinfo: too few fields in stat for pid %d", pid)
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Retry the read — a racing exit usually resolves to ENOENT or a fresh valid read on the next attempt
  2. Verify /proc/<pid>/stat actually contains a stat line (cat it manually) and that /proc is procfs (mount | grep proc)
  3. Treat the pid as gone and skip it in monitoring loops
  4. Check for fixtures/mocks if the failure happens in tests — a stub file without '(comm)' triggers this

Example fix

// before
lp := strings.Index(s, "(")
rp := strings.LastIndex(s, ")")
if lp < 0 || rp < 0 || rp <= lp {
    return nil, fmt.Errorf("procinfo: malformed stat for pid %d", pid)
}
// after: tolerate empty/truncated reads as transient
lp := strings.Index(s, "(")
rp := strings.LastIndex(s, ")")
if lp < 0 || rp < 0 || rp <= lp {
    if data == nil || len(data) == 0 {
        return nil, ErrNotFound // likely raced exit
    }
    return nil, fmt.Errorf("procinfo: malformed stat for pid %d: %q", pid, s)
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-validate the stat line shape before parsing pipelines that depend on it
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
if err == nil && !bytes.Contains(data, []byte("(")) {
    // malformed/transient; wait and re-read
}

Try / catch

_, err := procinfo.GetProcInfo(ctx, nil, pid)
if err != nil && strings.Contains(err.Error(), "malformed stat") {
    // likely transient (racing exit): retry once, then treat pid as gone
    time.Sleep(10 * time.Millisecond)
    info, err = procinfo.GetProcInfo(ctx, nil, pid)
}

Prevention

When it happens

Trigger: Reading a truncated or empty stat file (racing process exit can yield partial content in some kernels/filesystems); the path resolved to something that is not a proc stat file (e.g. procfs not mounted, an overlay exposing a different file); corrupted data from unusual virtualization layers.

Common situations: Flaky reads of a dying process's stat in a process monitor loop; misconfigured containers where /proc is not procfs (e.g. bind-mounted to a regular dir); testing with fake /proc fixtures that lack parentheses.

Understand the failure class

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/1148337d94962e0a. Report an issue: GitHub.