coleam00/Archon · error

Detached run control endpoint is already owned: ${path}

Error message

Detached run control endpoint is already owned: ${path}

What it means

When starting the detached-run control Unix socket server, if listen() fails with EADDRINUSE on POSIX the code checks whether the existing endpoint is connectable. A successful connection proves another live process already owns this run's control endpoint, so it throws instead of stealing the socket. Only a refused connection (crashed owner) allows removing the stale socket path and rebinding.

Source

Thrown at packages/cli/src/utils/detached-run-control.ts:152

  });
}

function isNodeError(error: unknown): error is NodeJS.ErrnoException {
  return error instanceof Error;
}

async function listenWithoutReplacingOwner(server: Server, path: string): Promise<void> {
  try {
    await listen(server, path);
    return;
  } catch (error) {
    if (!isNodeError(error) || error.code !== 'EADDRINUSE' || process.platform === 'win32') {
      throw error;
    }
  }

  if (await canConnect(path)) {
    throw new Error(`Detached run control endpoint is already owned: ${path}`);
  }

  // A crashed Unix owner can leave the socket pathname behind. Connection refusal,
  // not age, is the proof that no process owns it; remove only that stale pathname.
  rmSync(path, { force: true });
  await listen(server, path);
}

async function acquireOwnerLock(runId: string, endpointPath: string): Promise<number> {
  const lockPath = detachedRunControlLockPath(runId);
  try {
    return openSync(lockPath, 'wx', 0o600);
  } catch (error) {
    if (!isNodeError(error) || error.code !== 'EEXIST') throw error;
  }

  if (await canConnect(endpointPath)) {
    throw new Error(`Detached run control endpoint is already owned: ${endpointPath}`);

View on GitHub (pinned to 0773b97458)

Solutions

  1. Confirm the existing owner is alive and use it: query the run's status instead of starting a new control server.
  2. If you intended a new owner, first cancel/stop the existing owner process, then restart.
  3. Verify you are not starting the same run id twice (check your wrapper scripts or CI for duplicate launches).

Example fix

// before
archon run resume <runId>   # second instance -> endpoint already owned
// after
archon run stop <runId>     # terminate current owner
archon run resume <runId>
Defensive patterns

Strategy: retry

Validate before calling

// Before starting, test if an owner already answers:
const alive = await canConnect(socketPath); // your own helper
if (alive) console.log('Run already owned; attach/stop instead of starting.');

Try / catch

try {
  await startDetachedRunControlServer(...);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Detached run control endpoint is already owned')) {
    // fall back to attaching/querying the existing owner
    const pid = await getDetachedRunOwnerPid(runId);
  } else throw err;
}

Prevention

When it happens

Trigger: startDetachedRunControlServer is called while another live Archon process holds the control socket for the same run id, and canConnect(path) succeeds.

Common situations: Two instances of the same detached run started concurrently; trying to resume/control a run from a second terminal while the original owner process is still alive; a run id reused while the old owner is running.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/c269a61b17140e2a. Report an issue: GitHub.