{"record":{"id":"bd4076e76b046162","repo":"gastownhall/beads","slug":"procid-malformed-proc-stat-starttime-w","errorCode":null,"errorMessage":"procid: malformed proc stat starttime: %w","messagePattern":"procid: malformed proc stat starttime: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/procid/procid_linux.go","lineNumber":215,"sourceCode":"\treturn parseStartTime(string(data))\n}\n\nfunc parseStartTime(stat string) (string, error) {\n\tendComm := strings.LastIndex(stat, \")\")\n\tif endComm == -1 {\n\t\treturn \"\", errors.New(\"procid: malformed proc stat: missing comm terminator\")\n\t}\n\tfields := strings.Fields(stat[endComm+1:])\n\t// The remainder starts with state (field 3), so starttime (field 22) is\n\t// its twentieth field.\n\tif len(fields) < 20 {\n\t\treturn \"\", errors.New(\"procid: malformed proc stat: missing starttime\")\n\t}\n\tif fields[0] == \"Z\" || fields[0] == \"X\" || fields[0] == \"x\" {\n\t\treturn \"\", fmt.Errorf(\"procid: process is no longer running: %w\", unix.ESRCH)\n\t}\n\tif _, err := strconv.ParseUint(fields[19], 10, 64); err != nil {\n\t\treturn \"\", fmt.Errorf(\"procid: malformed proc stat starttime: %w\", err)\n\t}\n\treturn fields[19], nil\n}\n\ntype bootIDReadError struct {\n\tpath string\n\terr  error\n}\n\nfunc (e *bootIDReadError) Error() string {\n\treturn fmt.Sprintf(\"procid: read boot ID %s: %v\", e.path, e.err)\n}\n\ntype processStatReadError struct {\n\tpid int\n\terr error\n}\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/procid/procid_linux.go#L197-L233","documentation":"parseStartTime validates that the starttime field of /proc/<pid>/stat (field 22, its twentieth field after the state field due to the parentheses-stripping split) is a valid unsigned integer. If strconv.ParseUint fails, the /proc data was structurally unexpected — the format is kernel-defined, so this almost always indicates parsing drift (e.g. comm containing unhandled characters) or a non-Linux/non-standard /proc rather than a caller mistake.","triggerScenarios":"Reading /proc/<pid>/stat where the split produced fewer/misaligned fields than expected — typically when the process name (comm) contains characters the parser's parenthesis-stripping did not fully account for, a modified/hardened kernel altering stat layout, or reading from an unusual /proc (e.g. some sandboxed or emulated environments) with different field ordering.","commonSituations":"Running under gVisor, WSL1, or other compatibility layers whose /proc/stat differs; containers with masked /proc; older or patched kernels with layout differences; a bug in the field-splitting logic when comm contains ')' characters.","solutions":["Check the raw /proc/<pid>/stat content for the affected PID and confirm field 22 (starttime) is a number","Upgrade procid/beads to the latest version — comm-parsing fixes land frequently","If running under a sandbox (gVisor/WSL1), test on standard Linux or file an issue with the captured stat line","Note that the comm field can contain ')' — verify the parser takes the LAST ')' before splitting (parse after rindex of ')')"],"exampleFix":"// before\nfields := strings.Fields(string(data))\n// after\n// strip up to the LAST ')' so a comm like \"my)proc\" cannot shift fields\nif i := strings.LastIndex(string(data), \")\"); i >= 0 {\n\tfields = strings.Fields(string(data)[i+1:])\n}","handlingStrategy":"validation","validationCode":"func validateStatLine(data []byte) error {\n\ts := string(data)\n\ti := strings.LastIndexByte(s, ')')\n\tif i < 0 {\n\t\treturn errors.New(\"no closing paren in proc stat\")\n\t}\n\tfields := strings.Fields(s[i+1:])\n\tif len(fields) < 20 {\n\t\treturn errors.New(\"too few fields\")\n\t}\n\t_, err := strconv.ParseUint(fields[19], 10, 64)\n\treturn err\n}","typeGuard":null,"tryCatchPattern":"tok, err := procid.Capture(pid)\nif err != nil && strings.Contains(err.Error(), \"malformed proc stat\") {\n\t// log the raw /proc/<pid>/stat line and report a bug; do not trust the token\n\traw, _ := os.ReadFile(fmt.Sprintf(\"/proc/%d/stat\", pid))\n\tlog.Errorf(\"malformed stat for pid %d: %q: %v\", pid, raw, err)\n\treturn errUntrustedIdentity\n}","preventionTips":["Keep the library updated; comm-parsing bugs (e.g. ')' in process names) get fixed upstream","Avoid relying on /proc under sandboxes with divergent procfs (gVisor, WSL1) for identity","Log the raw stat line when this fires so kernel-layout differences can be diagnosed","Verify on a standard kernel before deploying to hardened/patched kernels"],"tags":["linux","procfs","parsing","malformed-data"],"backgroundTag":"proc-stat-malformed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}