paperclipai/paperclip · error · Error
Failed to start worktree port reservation lock probe at ${lo
Error message
Failed to start worktree port reservation lock probe at ${lockPath} What it means
Thrown by startRegistryLockHeartbeat when the heartbeat worker started (flag 1 set) but the probe port it reports is <= 0. The worker binds a TCP probe port that other processes use to verify the lock owner is truly alive; a worker that cannot bind reports no port, and the lock cannot be advertised, so acquisition aborts and cleans up.
Source
Thrown at packages/shared/src/worktree-port-registry.ts:285
heartbeatMs: WORKTREE_PORT_REGISTRY_LOCK_HEARTBEAT_MS,
lockPath,
ownerFiles: [
WORKTREE_PORT_REGISTRY_LOCK_OWNER_FILE,
WORKTREE_PORT_REGISTRY_LOCK_OWNER_BACKUP_FILE,
],
probeTimeoutMs: WORKTREE_PORT_REGISTRY_LOCK_PROBE_TIMEOUT_MS,
token,
},
});
Atomics.wait(control, 0, 0, 2_000);
if (Atomics.load(control, 0) !== 1) {
void worker.terminate();
throw new Error(`Failed to start worktree port reservation lock heartbeat at ${lockPath}`);
}
const probePort = Atomics.load(control, 2);
if (probePort <= 0) {
void worker.terminate();
throw new Error(`Failed to start worktree port reservation lock probe at ${lockPath}`);
}
return { token, worker, control, probePort };
}
function stopRegistryLockHeartbeat(lease: RegistryLockLease): void {
Atomics.store(lease.control, 1, 1);
Atomics.notify(lease.control, 1);
lease.worker.postMessage("stop");
if (Atomics.load(lease.control, 0) === 1) {
Atomics.wait(lease.control, 0, 1, 2_000);
}
void lease.worker.terminate();
}
function acquireRegistryLock(lockPath: string, deadline: number): RegistryLockLease | null {
try {
fs.mkdirSync(lockPath);
const token = `${process.pid}-${randomUUID()}`;View on GitHub (pinned to a7e689b3c3)
Solutions
- Retry the operation once; ephemeral port bind failures are frequently transient.
- Verify the environment allows binding 127.0.0.1 on an ephemeral port (check seccomp/firewall/apparmor profiles, docker network policies).
- Raise the file-descriptor limit (ulimit -n) if the process is FD-exhausted.
- Reduce simultaneous worktree operations that each start lock workers.
Defensive patterns
Strategy: retry
Validate before calling
// Preflight: can this process bind an ephemeral loopback port right now?
import net from "node:net";
function canBindLoopback(): Promise<boolean> {
return new Promise((resolve) => {
const s = net.createServer();
s.once("error", () => resolve(false));
s.listen(0, "127.0.0.1", () => s.close(() => resolve(true)));
});
} Try / catch
try {
return withWorktreePortRegistryLockSync(home, run);
} catch (error) {
if (error instanceof Error && error.message.includes("lock probe")) {
return withWorktreePortRegistryLockSync(home, run); // transient bind failure: retry once
}
throw error;
} Prevention
- Ensure the runtime allows loopback TCP binds (check seccomp/apparmor/docker policies).
- Raise ulimit -n if FD exhaustion is plausible.
- Retry once before escalating; ephemeral bind races are short-lived.
When it happens
Trigger: The worker's server.listen on an ephemeral port fails (EADDRNOTAVAIL, EACCES on restricted networks) or the listen callback never fires within the 2s readiness wait, leaving control[2] at 0; withWorktreePortRegistryLockSync then throws during lock acquisition.
Common situations: Containers/network namespaces with loopback binding restricted or no IPv4 stack ready; firewall or seccomp policies blocking socket creation; FD exhaustion (EMFILE) so listen fails; momentarily no free ports under heavy churn.
Related errors
- Failed to start worktree port reservation lock heartbeat at
- Cannot determine worktree port reservation lock owner identi
- Timed out waiting for worktree port reservation lock at ${lo
- Registered ${label} Paperclip config is missing its adjacent
- Registered ${label} Paperclip config has no PAPERCLIP_INSTAN
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/79433eb6a47efd54.
Report an issue: GitHub.