paperclipai/paperclip · error · Error

runner_prp_authority_already_registered

Error message

runner_prp_authority_already_registered

What it means

Thrown by registerRunnerPrpAuthority when a runner attempts to register as the live PRP authority for a runId that already has an active registration. Each run may have exactly one live authority at a time; duplicate registration is rejected to prevent two transports steering the same run.

Source

Thrown at server/src/realtime/runner-prp-ws.ts:131

    },
  );
}

export async function registerRunnerPrpAuthority(input: {
  readonly companyId: string;
  readonly issueId?: string;
  readonly agentId?: string;
  readonly runId: string;
  readonly authority: DurablePrpControlPlane;
}): Promise<{ readonly connectUrl: string; release(): Promise<void> }> {
  if (loopbackOrigin === null) {
    throw new Error("runner_prp_websocket_server_not_configured");
  }
  if (!UUID_PATTERN.test(input.runId) || input.companyId.length === 0) {
    throw new Error("runner_prp_authority_binding_invalid");
  }
  if (registrations.has(input.runId)) {
    throw new Error("runner_prp_authority_already_registered");
  }
  const generation = Symbol(input.runId);
  const registeredAuthority: RegisteredAuthority = {
    companyId: input.companyId,
    issueId: input.issueId ?? null,
    agentId: input.agentId ?? null,
    authority: input.authority,
    generation,
    runtimeRequestResolutions: new Map(),
  };
  registrations.set(input.runId, registeredAuthority);
  const currentKey = input.issueId && input.agentId
    ? liveAuthorityKey({
        companyId: input.companyId,
        issueId: input.issueId,
        agentId: input.agentId,
      })
    : null;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Wait for/deregister the existing authority before re-registering (check registrations for the runId first)
  2. Ensure the old runner process is terminated or its registration released on disconnect
  3. Use a single runner process per runId; guard against supervisor double-start
  4. Retry registration with backoff until the stale registration is cleaned up

Example fix

// before
registerRunnerPrpAuthority({ runId, companyId }); // throws if already registered
// after
try {
  registerRunnerPrpAuthority({ runId, companyId });
} catch (e) {
  if (e.message === 'runner_prp_authority_already_registered') {
    await waitForAuthorityRelease(runId);
    registerRunnerPrpAuthority({ runId, companyId });
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (registrations.has(runId)) await releaseAuthority(runId); // or wait before registering

Type guard

const canRegister = (runId, regs) => !regs.has(runId);

Try / catch

try { registerRunnerPrpAuthority(input) } catch (e) { if (e.message === 'runner_prp_authority_already_registered') { await backoffRetry(register, input); } else throw e; }

Prevention

When it happens

Trigger: A runner reconnects/retries its websocket and calls registerRunnerPrpAuthority again without the previous registration being torn down; two runner processes claim the same runId.

Common situations: Websocket reconnect storm after network blip where server-side cleanup lags; duplicated runner processes after a supervisor restart; run resumed in a second process while the first still holds the registration.

Related errors


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