paperclipai/paperclip · critical
Managed Codex credential ownership was lost
Error message
Managed Codex credential ownership was lost
What it means
Managed Codex credentials are guarded by a lease quorum of listening lock servers plus validity checks. assertHeld throws when ownership is no longer provable: the lease was released, a lock server reported the lease invalid, or fewer servers than CREDENTIAL_LEASE_QUORUM report listening. Called by inheritanceFds and activateLifetimeOwner, this means credential inheritance or lifetime-owner activation cannot proceed safely because another process may now own the credential.
Source
Thrown at packages/paperclip-runner/src/drivers/acpx/codex-credentials.ts:436
inheritanceFds = Object.freeze([first, second]) as readonly [
number,
number,
];
} catch (error) {
released = true;
await Promise.allSettled(servers.map(closeCredentialLeaseServer));
throw error;
}
return Object.freeze({
assertHeld(): void {
if (
released ||
invalid !== null ||
servers.filter((server) => server.listening).length <
CREDENTIAL_LEASE_QUORUM
) {
throw new Error("Managed Codex credential ownership was lost");
}
},
candidatePorts(): readonly [number, number, number] {
return candidatePorts;
},
inheritanceFds(): readonly [number, number] {
this.assertHeld();
return inheritanceFds;
},
async activateLifetimeOwner(pid: number): Promise<void> {
this.assertHeld();
if (!Number.isSafeInteger(pid) || pid < 1) {
throw new Error("Managed Codex credential lifetime owner is invalid");
}
},
async release(): Promise<void> {
if (released) return;
const outcomes = await Promise.allSettled(View on GitHub (pinned to 01ad858492)
Solutions
- Re-acquire the managed credential lease (re-run the credential acquisition/lock flow) before retrying inheritanceFds or activateLifetimeOwner.
- Check that all quorum lock server ports are listening; restart the lease servers and resolve any port conflicts (use candidatePorts() to pick free ports).
- Ensure only one runner instance owns the credential at a time; stop the competing process.
- Retry promptly after re-acquisition and keep the holder process alive through fork/inheritance to avoid releasing mid-operation.
Example fix
// before const fds = lease.inheritanceFds(); // lease already released -> throws // after if (!lease.isHeld()) await reAcquireCredentialLease(lease.candidatePorts()); const fds = lease.inheritanceFds();
Defensive patterns
Strategy: retry
Validate before calling
function leaseIsUsable(lease) {
return !lease.released && lease.invalid === null &&
lease.servers.filter((s) => s.listening).length >= CREDENTIAL_LEASE_QUORUM;
}
if (!leaseIsUsable(lease)) await reAcquireLease(); Type guard
function holdsQuorum(lease) {
return lease.servers.filter((s) => s.listening).length >= CREDENTIAL_LEASE_QUORUM;
} Try / catch
try {
const fds = lease.inheritanceFds();
} catch (err) {
if (err.message === "Managed Codex credential ownership was lost") {
await reAcquireCredentialLease(lease.candidatePorts());
// then retry inheritance once
} else throw err;
} Prevention
- Keep the lease-holder process alive through fork/inheritance operations.
- Pre-check quorum ports are listening before claiming the credential.
- Ensure single-instance ownership; use locks to prevent competing runners.
- Handle machine sleep/resume by re-validating the lease afterward.
When it happens
Trigger: Calling inheritanceFds() or activateLifetimeOwner() after the lease was released; a quorum lock server stopped listening; the lease was invalidated by another claimant winning the lifetime-owner election.
Common situations: Runner process restarts or fork timing causing the lease to lapse; port conflicts preventing quorum servers from listening; two runner instances contending for the same managed credential; machine sleep/resume expiring the lease.
Related errors
- Worktree seed source diagnostics changed while waiting for t
- run-dispatch: run ${input.runId} changed issue context repea
- ${prefix}: "captureCredential" must be a function when prese
- Failed to start worktree port reservation lock heartbeat at
- Timed out waiting for worktree port reservation lock at ${lo
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/40eabae63a739886.
Report an issue: GitHub.