{"record":{"id":"c45851593aab0404","repo":"stablyai/orca","slug":"benchmark-helper-signal-recovery-failed","errorCode":null,"errorMessage":"Benchmark helper signal recovery failed","messagePattern":"Benchmark helper signal recovery failed","errorType":"exception","errorClass":"AggregateError","httpStatus":null,"severity":"error","filePath":"config/scripts/macos-computer-helper-owner-loss-processes.mjs","lineNumber":376,"sourceCode":"    operations.signalProcess(-identity.pgid, signal)\n    if (signal !== 'SIGKILL') {\n      operations.signalProcess(-identity.pgid, 'SIGCONT')\n    }\n    stopped = false\n    return true\n  } catch (error) {\n    let resumeError\n    if (stopped) {\n      try {\n        operations.signalProcess(identity.pid, 'SIGCONT')\n      } catch (caught) {\n        if (caught?.code !== 'ESRCH') {\n          resumeError = caught\n        }\n      }\n    }\n    if (resumeError) {\n      throw new AggregateError([error, resumeError], 'Benchmark helper signal recovery failed')\n    }\n    if (error.code === 'ESRCH') {\n      return false\n    }\n    throw error\n  }\n}\n\nexport function killRecordedProcess(recordPath, expectedCommandFragment) {\n  if (!existsSync(recordPath)) {\n    return false\n  }\n  const record = JSON.parse(readFileSync(recordPath, 'utf8'))\n  if (!signalProcessIdentity(record, expectedCommandFragment, 'SIGKILL')) {\n    return false\n  }\n  return waitForIdentityExit(record)\n}","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/config/scripts/macos-computer-helper-owner-loss-processes.mjs#L358-L394","documentation":"Catch block of signalProcessIdentity: the main signal try-block threw, the helper had been SIGSTOP'd (stopped === true), and the SIGCONT recovery (operations.signalProcess(identity.pid, 'SIGCONT')) also threw a non-ESRCH error. Original plus resume error are wrapped in an AggregateError so the resume failure is not lost.","triggerScenarios":"operations.signalProcess(-identity.pgid, signal) throws, stopped is true, and the subsequent SIGCONT of identity.pid raises a non-ESRCH error (EPERM/EINVAL).","commonSituations":"Owner-loss again: by resume time the helper's uid changed. Also: signalProcess stubbed to always throw, or the pgid became invalid because the process group leader exited.","solutions":["Unpack AggregateError.errors: [0] is the original signal failure, [1] is the resume failure","After this throw, run killProcessMatchingCommand to sweep the helper by command fragment","Make the resume path tolerate EPERM when the trial is already aborting","Inject operations in tests to assert both signal and resume are exercised"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAggregateError(e) {\n  return e instanceof Error && Array.isArray(e.errors)\n}","tryCatchPattern":"try {\n  signalProcessIdentity(recorded, frag, sig)\n} catch (error) {\n  const causes = error instanceof AggregateError ? error.errors : [error]\n  // causes[0] = original signal error, causes[1] = resume error\n  killProcessMatchingCommand([frag])\n}","preventionTips":["Unpack AggregateError.errors before logging so the resume failure is not lost","Always follow with killProcessMatchingCommand as a command-based fallback","In tests, inject operations to exercise both signal and resume paths"],"tags":["process-management","error-recovery","aggregate","signals"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}