{"record":{"id":"fc6cf0974d12db39","repo":"coleam00/Archon","slug":"detached-run-control-endpoint-is-already-owned-fc6cf0","errorCode":null,"errorMessage":"Detached run control endpoint is already owned: ${endpointPath}","messagePattern":"Detached run control endpoint is already owned: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/utils/detached-run-control.ts","lineNumber":170,"sourceCode":"    throw new Error(`Detached run control endpoint is already owned: ${path}`);\n  }\n\n  // A crashed Unix owner can leave the socket pathname behind. Connection refusal,\n  // not age, is the proof that no process owns it; remove only that stale pathname.\n  rmSync(path, { force: true });\n  await listen(server, path);\n}\n\nasync function acquireOwnerLock(runId: string, endpointPath: string): Promise<number> {\n  const lockPath = detachedRunControlLockPath(runId);\n  try {\n    return openSync(lockPath, 'wx', 0o600);\n  } catch (error) {\n    if (!isNodeError(error) || error.code !== 'EEXIST') throw error;\n  }\n\n  if (await canConnect(endpointPath)) {\n    throw new Error(`Detached run control endpoint is already owned: ${endpointPath}`);\n  }\n  // A lock is written immediately before listen. Give that narrow startup window\n  // one chance to become reachable before treating both files as crash residue.\n  await new Promise<void>(resolve => setTimeout(resolve, POLL_INTERVAL_MS));\n  if (await canConnect(endpointPath)) {\n    throw new Error(`Detached run control endpoint is already owned: ${endpointPath}`);\n  }\n\n  rmSync(lockPath, { force: true });\n  if (process.platform !== 'win32') rmSync(endpointPath, { force: true });\n  return openSync(lockPath, 'wx', 0o600);\n}\n\nfunction releaseOwnerLock(lockPath: string, lockFd: number): void {\n  const owned = fstatSync(lockFd);\n  closeSync(lockFd);\n  try {\n    const current = statSync(lockPath);","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/cli/src/utils/detached-run-control.ts#L152-L188","documentation":"acquireOwnerLock() creates the owner lock file exclusively (openSync 'wx'); on EEXIST it checks whether the control endpoint is still connectable. If a live process answers on the endpoint, the run is already owned and the first of two identical 'already owned' errors is thrown. Only after a failed connect (and one poll grace period for startup races) may the stale lock be removed.","triggerScenarios":"lockFd is called for a run whose lock file exists and whose control socket accepts a connection when acquireOwnerLock probes it.","commonSituations":"Attaching to a run whose owner process is still alive from a previous session; a wrapper script re-running the same detached run; leftover lock plus a still-running owner you forgot about.","solutions":["Find the live owner and use it (check ps for the Archon process serving this run; or ask the existing endpoint for its pid).","Stop the existing owner (archon run stop <runId>) before re-acquiring.","If the process shown is genuinely dead but the socket still accepts, investigate for another process holding the port; only then clean the stale files."],"exampleFix":"// before\narchon run attach <runId>   # owner alive -> already owned\n// after\narchon run stop <runId>\narchon run attach <runId>","handlingStrategy":"try-catch","validationCode":"const ownerAlive = await canConnect(endpointPath);\nif (ownerAlive && existsSync(lockPath)) {\n  console.log('Run already owned; do not acquire lock.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  const fd = await acquireOwnerLock(runId, endpointPath);\n} catch (err) {\n  if (err instanceof DetachedRunOwnerUnavailableError || (err instanceof Error && err.message.includes('already owned'))) {\n    // attach to existing owner or stop it first\n  } else throw err;\n}","preventionTips":["Use the public run-control API (stop/attach) instead of hand-rolling lock acquisition.","Never delete lock/socket files while a live owner may exist — probe connectivity first.","Guard scripts against concurrent invocations on the same run id."],"tags":["ipc","unix-socket","detached-run","locking"],"backgroundTag":"address-already-in-use","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}