{"record":{"id":"280536e4652bd370","repo":"stablyai/orca","slug":"benchmark-pending-anchor-recovery-failed","errorCode":null,"errorMessage":"Benchmark pending anchor recovery failed","messagePattern":"Benchmark pending anchor recovery failed","errorType":"exception","errorClass":"AggregateError","httpStatus":null,"severity":"error","filePath":"config/scripts/macos-computer-helper-owner-loss-processes.mjs","lineNumber":271,"sourceCode":"    const ownershipError = new Error('Benchmark process group no longer belongs to this trial')\n    const recoveryErrors = compensateStoppedGroup(pgid, groupState, operations)\n    if (recoveryErrors.length > 0) {\n      throw new AggregateError(\n        [ownershipError, ...recoveryErrors],\n        'Benchmark process group authority recovery failed'\n      )\n    }\n    throw ownershipError\n  }\n  if (groupState.anchorPid) {\n    try {\n      operations.signalProcess(groupState.anchorPid, 'SIGCONT')\n      groupState.anchorPid = null\n    } catch (error) {\n      if (error?.code === 'ESRCH') {\n        groupState.anchorPid = null\n      } else {\n        throw new AggregateError([error], 'Benchmark pending anchor recovery failed')\n      }\n    }\n  }\n  const anchor = members[0]\n  try {\n    operations.signalProcess(anchor.pid, 'SIGSTOP')\n    groupState.anchorPid = anchor.pid\n    const stoppedAnchor = operations\n      .processIdentities(true)\n      .find((identity) => identity.pid === anchor.pid)\n    if (!sameIdentity(stoppedAnchor, anchor)) {\n      throw new Error('Benchmark process group anchor changed before signaling')\n    }\n    operations.signalProcess(-pgid, 'SIGSTOP')\n    groupState.stopped = true\n    groupState.anchorPid = null\n    const stoppedMembers = operations\n      .processIdentities(true)","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/macos-computer-helper-owner-loss-processes.mjs#L253-L289","documentation":"Thrown by signalValidatedProcessGroup when resuming a previously-SIGSTOP'd anchor process (groupState.anchorPid) fails with an error other than ESRCH. The underlying signal error is wrapped in an AggregateError so the original cause survives. ESRCH is tolerated because a vanished process is the expected race during teardown; any other failure (EPERM, EINVAL) means the anchor is in an unexpected state and the trial must abort.","triggerScenarios":"A prior call left groupState.anchorPid set (anchor SIGSTOP'd), and the follow-up operations.signalProcess(anchorPid, 'SIGCONT') throws a non-ESRCH error. Concretely: the anchor's owning uid changed (EPERM), the pid is invalid (EINVAL), or a test-injected operations.signalProcess stub threw without an ESRCH code.","commonSituations":"The macOS owner-loss scenario the file is named for — a helper process's owner changes between trials and resuming it raises EPERM. Also seen with a groupState object reused across trials without being reset, or with a signalProcess stub in unit tests that does not emulate ESRCH for dead pids.","solutions":["Reset groupState to { stopped: false, anchorPid: null } between independent trials so no stale anchor is resumed","Before the second call, verify the anchor is still owned by the benchmark user via processIdentity plus a uid check","In tests, make the stubbed signalProcess rethrow only ESRCH-coded errors for dead pids and swallow nothing else","Catch AggregateError at the trial boundary and run compensateStoppedGroup followed by killProcessMatchingCommand during cleanup"],"exampleFix":"// before\nsignalValidatedProcessGroup(pgid, frag, 'SIGTERM', groupState)\n// groupState.anchorPid left set from the prior run -> SIGCONT throws EPERM\n\n// after\nconst groupState = { stopped: false, anchorPid: null }\nsignalValidatedProcessGroup(pgid, frag, 'SIGTERM', groupState)","handlingStrategy":"try-catch","validationCode":"function safeToResumeAnchor(groupState, operations) {\n  if (!groupState.anchorPid) return true\n  const id = operations.processIdentity(groupState.anchorPid)\n  return id && id.command.includes(environmentFragment)\n}\n// call before signalValidatedProcessGroup:\nif (!safeToResumeAnchor(groupState, processGroupSignalOperations)) {\n  groupState.anchorPid = null // give up on stale anchor\n}","typeGuard":"function isAggregateError(e) {\n  return e instanceof Error && Array.isArray(e.errors)\n}","tryCatchPattern":"try {\n  signalValidatedProcessGroup(pgid, frag, sig, groupState)\n} catch (error) {\n  const causes = error instanceof AggregateError ? error.errors : [error]\n  for (const c of causes) log.error(c)\n  killProcessMatchingCommand([frag])\n}","preventionTips":["Reset groupState to { stopped: false, anchorPid: null } at the start of every trial","Never reuse a groupState across unrelated helper spawns","Inject a stubbed operations object in tests and assert ESRCH is the only swallowed code"],"tags":["process-management","signals","macos","owner-loss","aggregate"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}