{"record":{"id":"29883be5cb827970","repo":"stablyai/orca","slug":"recorded-benchmark-helper-identity-pid-did-not","errorCode":null,"errorMessage":"Recorded benchmark helper ${identity.pid} did not exit","messagePattern":"Recorded benchmark helper (.+?) did not exit","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"config/scripts/macos-computer-helper-owner-loss-processes.mjs","lineNumber":148,"sourceCode":"    .split('\\n')\n    .map((line) => line.trim().match(/^(\\d+)\\s+(\\d+)\\s+(.+)$/))\n    .filter(Boolean)\n    .map((match) => ({\n      pid: Number(match[1]),\n      pgid: Number(match[2]),\n      command: match[3]\n    }))\n}\n\nfunction waitForIdentityExit(identity) {\n  const deadline = Date.now() + PROCESS_EXIT_TIMEOUT_MS\n  while (Date.now() < deadline) {\n    if (!processIdentityIsCurrent(identity)) {\n      return true\n    }\n    sleepSync(PROCESS_POLL_MS)\n  }\n  throw new Error(`Recorded benchmark helper ${identity.pid} did not exit`)\n}\n\nexport function spawnBenchmarkProcess(executable, args, options) {\n  return spawnSync(executable, args, {\n    ...options,\n    detached: true,\n    killSignal: 'SIGKILL'\n  })\n}\n\nexport function runBenchmarkCleanupStages(stages) {\n  const errors = []\n  for (const stage of stages) {\n    try {\n      stage()\n    } catch (error) {\n      errors.push(error)\n    }","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/macos-computer-helper-owner-loss-processes.mjs#L130-L166","documentation":"waitForIdentityExit polls processIdentityIsCurrent every 25ms (PROCESS_POLL_MS) for up to 2s (PROCESS_EXIT_TIMEOUT_MS) using a synchronous Atomics.wait loop. If the process identity still matches after the deadline, the process did not exit and the error fires.","triggerScenarios":"After sending SIGKILL (or a signal that was ignored), the helper process is still alive after 2s. Causes: process stuck in uninterruptible sleep (D state, waiting on I/O); zombie not reaped by parent; signal was not delivered due to permission issues; the process group signal (-pgid) did not reach the target.","commonSituations":"Helper is blocked on a filesystem operation in D state; the parent (Electron) is not reaping the zombie; SIGKILL sent to the wrong pgid; macOS App Nap or suspension preventing signal delivery.","solutions":["Check the process state with `ps -o pid,stat,command -p <pid>` — D state means uninterruptible I/O","Verify the parent process is alive and reaping zombies (kill the parent if it's stuck)","Confirm the signal was sent to the correct PID/pgid — check for permission errors in the kill call","If the process legitimately needs more time, consider increasing PROCESS_EXIT_TIMEOUT_MS (but investigate the root cause first)"],"exampleFix":"// before: fixed 2s timeout may be too short for D-state processes\nconst PROCESS_EXIT_TIMEOUT_MS = 2_000\n\n// after: escalate signal and extend deadline for stubborn processes\nfunction waitForIdentityExit(identity, timeoutMs = 5_000) {\n  const deadline = Date.now() + timeoutMs\n  while (Date.now() < deadline) {\n    if (!processIdentityIsCurrent(identity)) return true\n    sleepSync(PROCESS_POLL_MS)\n  }\n  throw new Error(`Recorded benchmark helper ${identity.pid} did not exit`)\n}","handlingStrategy":"retry","validationCode":"// Check process state before waiting — D-state processes cannot be killed\nfunction canExitFast(identity) {\n  const identity0 = processIdentity(identity.pid)\n  if (!identity0) return true\n  const stat = execFileSync('ps', ['-o', 'stat=', '-p', String(identity.pid)], { encoding: 'utf8' }).trim()\n  return !stat.includes('D') // D = uninterruptible sleep\n}","typeGuard":null,"tryCatchPattern":"try {\n  waitForIdentityExit(identity)\n} catch (error) {\n  if (error.message.includes('did not exit')) {\n    // escalate: verify SIGKILL was delivered, check for D state\n    const stat = execFileSync('ps', ['-o', 'stat=', '-p', String(identity.pid)], { encoding: 'utf8' }).trim()\n    if (stat.includes('D')) {\n      console.warn('Process in uninterruptible sleep — cannot be killed until I/O completes')\n    }\n  }\n  throw error\n}","preventionTips":["Verify the signal was delivered to the correct PID/pgid before waiting","Check for D state (uninterruptible sleep) before expecting prompt exit","Ensure the parent process is alive to reap zombies"],"tags":["process-lifecycle","timeout"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}