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
- Wait for/deregister the existing authority before re-registering (check registrations for the runId first)
- Ensure the old runner process is terminated or its registration released on disconnect
- Use a single runner process per runId; guard against supervisor double-start
- 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
- Run exactly one runner process per runId
- Release authority registration on runner shutdown (SIGTERM handlers)
- Use jittered backoff on websocket reconnect before re-registering
- Guard supervisors against double-starting runners for the same run
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
- Registered base project workspace does not exist at ${resolv
- Registered base project workspace must be canonical and cann
- Registered base project workspace is not a directory at ${ca
- Managed worktree seed registration is incomplete; project wo
- Worktree seed manifest target instance does not match the re
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/85fa2a3c38d1637b.
Report an issue: GitHub.