{"record":{"id":"651869a4174b0fef","repo":"stablyai/orca","slug":"could-not-read-cpu-time-for-pid-pid","errorCode":null,"errorMessage":"Could not read CPU time for PID ${pid}","messagePattern":"Could not read CPU time for PID (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/hang-watchdog-process-metrics.mjs","lineNumber":77,"sourceCode":"  if (!raw.trim()) {\n    return null\n  }\n  const parts = raw.split(':').map(Number)\n  if (!parts.length || parts.some((part) => !Number.isFinite(part))) {\n    return null\n  }\n  const seconds = parts.reduce((total, part) => total * 60 + part, 0)\n  const milliseconds = seconds * 1_000\n  return milliseconds >= 0 ? milliseconds : null\n}\n\nfunction processCpuTimeMs(pid) {\n  const raw = execFileSync('ps', ['-o', 'time=', '-p', String(pid)], {\n    encoding: 'utf8'\n  }).trim()\n  const milliseconds = parseProcessCpuTimeMs(raw)\n  if (milliseconds === null) {\n    throw new Error(`Could not read CPU time for PID ${pid}`)\n  }\n  return milliseconds\n}\n\nfunction combinedCpuTimeMs(pids) {\n  return pids.reduce((total, pid) => total + processCpuTimeMs(pid), 0)\n}\n\nexport async function sampleProductionPerformance(boundary, options) {\n  const loopDelay = monitorEventLoopDelay({ resolution: 10 })\n  let heartbeatCount = 0\n  const heartbeat = setInterval(() => {\n    heartbeatCount += 1\n    boundary.sendHeartbeat()\n  }, options.heartbeatIntervalMs)\n  const cpuBefore = combinedCpuTimeMs(boundary.pids)\n  loopDelay.enable()\n  try {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/hang-watchdog-process-metrics.mjs#L59-L95","documentation":"Thrown by processCpuTimeMs() when `ps -o time= -p <pid>` returns empty output or a value parseProcessCpuTimeMs rejects (non-numeric time components). CPU time (HH:MM:SS or MM:SS) is converted to milliseconds to measure watchdog overhead.","triggerScenarios":"The PID exited before the read (ps returns empty for a dead PID), the time format includes unexpected tokens, or ps column output is empty for a just-started process with zero accumulated CPU time on some platforms.","commonSituations":"Child process exiting between the before/after CPU samples, race during shutdown, or platform ps behavior differences for processes with no accumulated time.","solutions":["Verify the PID is alive before sampling CPU time.","Ensure no external signal kills the process during the sampling window.","Debug with a manual `ps -o time= -p <pid>` during a trial."],"exampleFix":"// before\nconst cpu = processCpuTimeMs(pid)  // throws if pid dead\n// after\ntry { process.kill(pid, 0) } catch { throw new Error(`pid ${pid} exited before CPU sample`) }\nconst cpu = processCpuTimeMs(pid)","handlingStrategy":"validation","validationCode":"function isAlive(pid: number): boolean { try { process.kill(pid, 0); return true } catch { return false } }\nif (!isAlive(pid)) throw new Error(`pid ${pid} exited before CPU time sample`)","typeGuard":"const isProcessAlive = (pid: number): boolean => { try { process.kill(pid, 0); return true } catch { return false } }","tryCatchPattern":null,"preventionTips":["Take before/after CPU samples while the process is guaranteed alive.","Guard against races by checking liveness immediately before each ps call."],"tags":["metrics","process","macos","ps","cpu","race-condition"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}