paperclipai/paperclip · error

native_runner_warm_attachment_ambiguous: expected one authen

Error message

native_runner_warm_attachment_ambiguous: expected one authenticated runner, found ${connectionCount}

What it means

During warm attachment exactly one authenticated runner connection is expected. If the core reports more than one authenticated runner connection, the transport cannot tell which one holds authority and throws, embedding the observed connection count.

Source

Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:3577

  }

  async #awaitWarmRunnerConnection(deadline: number): Promise<void> {
    const core = this.#core;
    if (core === null) throw new Error("native_runner_authority_unavailable");
    let reportedReconnectWait = false;
    while (Date.now() < deadline) {
      this.#throwIfFailed();
      const connectionCount = core.activeRunnerConnectionCount();
      if (connectionCount === 1) {
        if (reportedReconnectWait) {
          this.#diagnostic(
            "warm runner re-authenticated before authority rotation",
          );
        }
        return;
      }
      if (connectionCount > 1) {
        throw new Error(
          `native_runner_warm_attachment_ambiguous: expected one authenticated runner, found ${connectionCount}`,
        );
      }
      if (!reportedReconnectWait) {
        reportedReconnectWait = true;
        this.#diagnostic(
          "warm runner connection interrupted; waiting for re-authentication before authority rotation",
        );
      }
      if (await this.#runnerHasExited()) {
        throw new Error(
          "native_runner_warm_attachment_runner_exited: runner exited before authority rotation",
        );
      }
      await Promise.race([
        new Promise<void>((resolveWait) => setTimeout(resolveWait, 25)),
        this.#failureSignal,
      ]);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Kill the stale/duplicate runner process so only one remains
  2. Verify only one runnerd instance is launched per run (check process list / supervision config)
  3. Retry rotation after the old connection has fully torn down
  4. Report a bug if the transport itself is creating duplicate connections

Example fix

// before
// rotation attempted immediately after spawn
spawnRunner();
await transport.rotateAuthority();
// after
spawnRunner();
await waitFor(() => transport.activeRunnerConnectionCount() === 1, { timeout: 5000 });
await transport.rotateAuthority();
Defensive patterns

Strategy: validation

Validate before calling

const count = transport.activeRunnerConnectionCount?.() ?? 0;
if (count > 1) throw new Error(`duplicate runner connections: ${count}`);

Try / catch

try {
  await transport.rotateAuthority();
} catch (e) {
  if (String(e.message).startsWith('native_runner_warm_attachment_ambiguous')) {
    await killDuplicateRunners();
    await retryRotation();
  } else throw e;
}

Prevention

When it happens

Trigger: #awaitWarmRunnerConnection polls activeRunnerConnectionCount() and observes >1 while waiting for the old runner to be replaced by the re-authenticated one; duplicate runner processes authenticating during the rotation window.

Common situations: Two runner processes launched by accident (stale runner still alive plus a new one); a reconnect race where the old connection hasn't been dropped when the new one authenticates.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/a59d7722e269bec1. Report an issue: GitHub.