paperclipai/paperclip · error · Error
Failed to start worktree port reservation lock heartbeat at
Error message
Failed to start worktree port reservation lock heartbeat at ${lockPath} What it means
Thrown by startRegistryLockHeartbeat in the worktree port registry when the heartbeat Worker thread fails to signal readiness within 2 seconds (control flag never reaches 1). The heartbeat worker refreshes the lock's owner files and answers liveness probes; without it the lock cannot be held safely, so acquisition aborts and the freshly created lock directory is removed.
Source
Thrown at packages/shared/src/worktree-port-registry.ts:280
const worker = new Worker(REGISTRY_LOCK_HEARTBEAT_SOURCE, {
eval: true,
execArgv: [],
workerData: {
control: control.buffer,
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();
}View on GitHub (pinned to a7e689b3c3)
Solutions
- Retry the worktree/port-registry operation once; transient thread-start starvation is the most common cause.
- Check thread/resource limits in the environment (ulimit -u, cgroup pids limit, available memory) and raise them.
- Verify the Node.js version supports worker_threads and that no NODE_OPTIONS/execArgv flags interfere with eval workers.
- Reduce concurrent worktree operations hammering the registry at the same moment.
Defensive patterns
Strategy: retry
Validate before calling
// Before heavy fan-out, confirm worker threads can start in this environment:
import { isMainThread, Worker } from "node:worker_threads";
function workersAvailable(): Promise<boolean> {
return new Promise((resolve) => {
try {
const w = new Worker("Atomics.store(new Int32Array(workerData.b),0,1)", { eval: true, workerData: { b: new SharedArrayBuffer(4) } });
w.on("error", () => resolve(false));
w.on("exit", (c) => resolve(c === 0));
} catch {
resolve(false);
}
});
} Try / catch
try {
return withWorktreePortRegistryLockSync(home, run);
} catch (error) {
if (error instanceof Error && error.message.includes("heartbeat")) {
return withWorktreePortRegistryLockSync(home, run); // one retry for thread-start starvation
}
throw error;
} Prevention
- Keep concurrent worktree/port-registry operations modest on loaded CI machines.
- Check ulimit -u / cgroup pids.max before fan-out.
- Avoid exotic execArgv/NODE_OPTIONS that break eval-mode workers.
- Monitor and free FDs/memory; resource exhaustion often shows up first as worker spawn failures.
When it happens
Trigger: withWorktreePortRegistryLockSync spins up the eval-mode Worker; the worker throws during startup (bad embedded source, Node flags incompatibility), or the process/system is so loaded the thread cannot start and touch the SharedArrayBuffer within the 2s Atomics.wait budget.
Common situations: Heavily loaded CI machines or containers with CPU/thread limits (cgroup pids.max, low ulimit) preventing thread spawn; restrictive sandboxed runtimes where worker_threads spawning is disabled; Node versions or execArgv (e.g. --experimental flags, inspector) that break eval workers; transient fork/resource exhaustion (EMFILE, ENOMEM).
Related errors
- Timed out waiting for worktree port reservation lock at ${lo
- Failed to start worktree port reservation lock probe at ${lo
- Cannot determine worktree port reservation lock owner identi
- Cannot seed target embedded PostgreSQL at ${dataDir} while i
- Worktree seed source diagnostics changed while waiting for t
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-21).
Data as JSON: /api/errors/579191e554e4f747.
Report an issue: GitHub.