stablyai/orca · error · AggregateError
Benchmark process group signal recovery failed
Error message
Benchmark process group signal recovery failed
What it means
Outer catch of signalValidatedProcessGroup: the main signaling try-block threw, then compensateStoppedGroup (which SIGCONTs both the stopped group and any anchor pid) itself returned one or more non-ESRCH errors. The original error and every recovery error are combined into an AggregateError so neither is hidden.
Source
Thrown at config/scripts/macos-computer-helper-owner-loss-processes.mjs:309
if (
stoppedMembers.length === 0 ||
stoppedMembers.some((identity) => !identity.command.includes(environmentFragment))
) {
throw new Error('Benchmark process group changed before signaling')
}
if (signal !== 'SIGSTOP') {
operations.signalProcess(-pgid, signal)
if (signal !== 'SIGKILL') {
operations.signalProcess(-pgid, 'SIGCONT')
}
groupState.stopped = false
groupState.anchorPid = null
}
return true
} catch (error) {
const recoveryErrors = compensateStoppedGroup(pgid, groupState, operations)
if (recoveryErrors.length > 0) {
throw new AggregateError(
[error, ...recoveryErrors],
'Benchmark process group signal recovery failed'
)
}
if (error.code === 'ESRCH') {
return false
}
throw error
}
}
export function writeProcessRecord(recordPath, processIdentity) {
const temporaryPath = `${recordPath}.${process.pid}.tmp`
writeFileSync(temporaryPath, JSON.stringify(processIdentity))
renameSync(temporaryPath, recordPath)
}
export function processIdentityIsCurrent(identity) {View on GitHub (pinned to 1136503c6a)
Solutions
- Inspect AggregateError.errors — the first element is the root cause, the rest are recovery failures
- After this throw, fall back to killProcessMatchingCommand(environmentFragment) to sweep stray helpers
- Ensure compensateStoppedGroup treats EPERM on a uid-changed process as non-fatal if the trial is already being abandoned
- Reproduce with the operations parameter injected to log every signal and its target identity
Example fix
// before
try {
signalValidatedProcessGroup(pgid, frag, sig, state)
} catch (error) {
console.error(error.message)
}
// after
try {
signalValidatedProcessGroup(pgid, frag, sig, state)
} catch (error) {
const causes = error instanceof AggregateError ? error.errors : [error]
for (const cause of causes) console.error(cause)
killProcessMatchingCommand([frag])
} Defensive patterns
Strategy: try-catch
Type guard
function isAggregateError(e) {
return e instanceof Error && Array.isArray(e.errors)
} Try / catch
try {
signalValidatedProcessGroup(pgid, frag, sig, state)
} catch (error) {
const causes = error instanceof AggregateError ? error.errors : [error]
// causes[0] is the original; causes.slice(1) are recovery failures
for (const c of causes) log.error(c)
killProcessMatchingCommand([frag])
} Prevention
- Always pair signalValidatedProcessGroup with a killProcessMatchingCommand fallback
- Inspect AggregateError.errors in order: root cause first, recovery failures after
- In tests, inject operations to assert the recovery path is exercised
When it happens
Trigger: Any throw from the anchor-stop or group-signal path (141, 142, or a raw signal error) AND compensateStoppedGroup records errors — i.e. SIGCONT of -pgid or the anchor throws something other than ESRCH.
Common situations: The owner-loss scenario again: by recovery time the helper's uid has changed, so both the original signal and the resume raise EPERM. Also triggered by tests that inject a signalProcess stub failing on every call.
Related errors
- Benchmark helper signal recovery failed
- Benchmark pending anchor recovery failed
- Benchmark helper cleanup failed
- Benchmark process group anchor changed before signaling
- Benchmark process group changed before signaling
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/0508f3e5e313a503.
Report an issue: GitHub.