{"record":{"id":"081b8a0f64270313","repo":"coleam00/Archon","slug":"refusing-detached-run-control-because-process-st","errorCode":null,"errorMessage":"Refusing detached run control because process ${String(process.pid)} does not own process group ${String(process.pid)}","messagePattern":"Refusing detached run control because process (.+?) does not own process group (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/detached-run-control.ts","lineNumber":77,"sourceCode":"  if (process.platform !== 'win32') {\n    const stat = lstatSync(directory);\n    if (!stat.isDirectory() || stat.isSymbolicLink()) {\n      throw new Error(`Detached run control path is not a directory: ${directory}`);\n    }\n    if (uid !== undefined && stat.uid !== uid) {\n      throw new Error(`Detached run control directory is owned by another user: ${directory}`);\n    }\n    if ((stat.mode & 0o077) !== 0) {\n      throw new Error(`Detached run control directory must have mode 0700: ${directory}`);\n    }\n  }\n  return directory;\n}\n\n/** Prove the marked POSIX owner has the process group that active cancellation will signal. */\nexport function assertDetachedRunProcessOwner(): void {\n  if (process.platform !== 'win32' && !processGroupExists(process.pid)) {\n    throw new Error(\n      `Refusing detached run control because process ${String(process.pid)} does not own process group ${String(process.pid)}`\n    );\n  }\n}\n\n/** A bounded, user-scoped endpoint: Unix socket on POSIX, named pipe on Windows. */\nexport function detachedRunControlPath(runId: string): string {\n  const token = endpointToken(runId);\n  if (process.platform === 'win32') return `\\\\\\\\.\\\\pipe\\\\archon-workflow-${token}`;\n  return join(controlDirectory(), `${token}.sock`);\n}\n\nfunction detachedRunControlLockPath(runId: string): string {\n  return join(controlDirectory(), `${endpointToken(runId)}.lock`);\n}\n\nfunction listen(server: Server, path: string): Promise<void> {\n  return new Promise((resolve, reject) => {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/cli/src/utils/detached-run-control.ts#L59-L95","documentation":"assertDetachedRunProcessOwner() verifies, before enabling detached-run control, that the current process is actually the leader of its own process group (processGroupExists(pid) on POSIX). Detached-run cancellation works by signaling the owner's process group; if the process does not lead that group, signaling would hit the wrong processes, so the runtime refuses. Windows skips this check.","triggerScenarios":"Starting a detached run control server (via runWorkflowWithOwnedSource) on POSIX when the process is not a process-group leader — e.g. it was launched in a way that did not call setpgid(0,0).","commonSituations":"Embedding the Archon CLI inside another process manager, shell job-control setup, or test harness that runs the process in the parent's process group instead of a fresh one; environments where setpgid failed silently at startup.","solutions":["Launch the detached run through the normal Archon CLI entry point, which sets up its own process group.","If embedding programmatically, ensure the child is started detached with its own process group (e.g. spawn with detached:true on POSIX).","Check how your wrapper (docker exec, tmux, CI runner) starts the process and confirm it allows a new process group."],"exampleFix":"// before\nspawn('archon', ['run', ...])               // shares parent's pgroup\n// after\nspawn('archon', ['run', ...], { detached: true, stdio: 'ignore' }).unref()","handlingStrategy":"try-catch","validationCode":"function isGroupLeader(): boolean {\n  if (process.platform === 'win32') return true;\n  return process.getpid() === process.getpgid(process.getpid());\n}\nif (!isGroupLeader()) throw new Error('Refusing detached run: not a process-group leader');","typeGuard":null,"tryCatchPattern":"try {\n  assertDetachedRunProcessOwner();\n  await runWorkflowWithOwnedSource(...);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('does not own process group')) {\n    console.error('Launch the CLI detached (own process group), e.g. spawn(..., { detached: true }).');\n  } else throw err;\n}","preventionTips":["Always launch detached runs via the Archon CLI entry point rather than embedding in-process.","Use spawn detached:true when wrapping programmatically on POSIX.","Avoid process managers that force children into the parent's process group for this path."],"tags":["process","posix","detached-run"],"backgroundTag":"process-group-ownership","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}