gastownhall/beads · warning
procid: malformed proc stat: missing comm terminator
Error message
procid: malformed proc stat: missing comm terminator
What it means
parseStartTime parses /proc/<pid>/stat to extract field 22 (starttime). The kernel splits the stat line after the comm field, which is wrapped in parentheses; comm can contain spaces, so the parser uses the LAST ')' as the terminator. This error means no ')' was found at all, so the read stat content is not a well-formed stat line.
Source
Thrown at internal/procid/procid_linux.go:203
}
}
func isFatalSignal(sig syscall.Signal) bool {
return sig == syscall.SIGKILL || sig == syscall.SIGTERM
}
func processStartTime(pid int) (string, error) {
data, err := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat")
if err != nil {
return "", &processStatReadError{pid: pid, err: err}
}
return parseStartTime(string(data))
}
func parseStartTime(stat string) (string, error) {
endComm := strings.LastIndex(stat, ")")
if endComm == -1 {
return "", errors.New("procid: malformed proc stat: missing comm terminator")
}
fields := strings.Fields(stat[endComm+1:])
// The remainder starts with state (field 3), so starttime (field 22) is
// its twentieth field.
if len(fields) < 20 {
return "", errors.New("procid: malformed proc stat: missing starttime")
}
if fields[0] == "Z" || fields[0] == "X" || fields[0] == "x" {
return "", fmt.Errorf("procid: process is no longer running: %w", unix.ESRCH)
}
if _, err := strconv.ParseUint(fields[19], 10, 64); err != nil {
return "", fmt.Errorf("procid: malformed proc stat starttime: %w", err)
}
return fields[19], nil
}
type bootIDReadError struct {
path stringView on GitHub (pinned to 71377f2769)
Solutions
- Retry the read: re-read /proc/<pid>/stat; a transient truncation usually resolves or turns into a clean ESRCH/'no longer running' condition
- Check that the target PID still exists (os.Stat on /proc/<pid>) before parsing and treat failure as process-gone
- Verify you are reading /proc/<pid>/stat (not statm/status) and that the read returned a full buffer
- If running in a container, confirm /proc is the host or namespace proc you expect and not a masked path
Example fix
// before
data, _ := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
start, err := parseStartTime(string(data))
// after
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
if err != nil { return "", err } // PID gone -> treat as ESRCH
if !strings.Contains(string(data), ")") { return processStartTime(pid) } // retry once on truncation
start, err := parseStartTime(string(data)) Defensive patterns
Strategy: retry
Validate before calling
if _, err := os.Stat(fmt.Sprintf("/proc/%d/stat", pid)); err != nil { return ErrProcessGone } Type guard
func isStatTruncated(err error) bool { return strings.Contains(err.Error(), "missing comm terminator") } Try / catch
start, err := processStartTime(pid)
if err != nil {
if isStatTruncated(err) || errors.Is(err, unix.ESRCH) { return retryOnce(pid) }
return err
} Prevention
- Always re-read /proc/<pid>/stat fresh per call; never cache contents
- Check process existence (os.Stat /proc/<pid>) before parsing
- Expect races with short-lived processes and retry transient parse failures once
- In containers, verify /proc is not masked before using procid
When it happens
Trigger: Calling processStartTime (directly or via an anonymous caller) for a PID whose /proc/<pid>/stat read returned malformed or truncated content — typically a race where the process exits and the kernel zeroes/truncates the buffer mid-read, or reading the wrong file entirely.
Common situations: Short-lived child processes exiting between open() and read() of /proc; containerized environments where /proc is masked or virtualized; racy PID reuse checks in supervisory code.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- procid: malformed proc stat: missing starttime
- server: DoltServer.Start: capture child birth identity: %w
- procid: process %d still matches token after fatal signal an
- procid: process is no longer running: %w
- procid: malformed proc stat starttime: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/baaaae86f1976445.
Report an issue: GitHub.