Yeachan-Heo/oh-my-codex · error · Error
detached leader release timed out
Error message
detached leader release timed out
What it means
The leader polls release/abort report files on a 20ms loop until a deadline; if neither appears in time, it throws this timeout.
Source
Thrown at src/cli/index.ts:7105
const abortPath = `${payload.readyPath}.abort`;
const releaseDeadline = Date.now() + 30_000;
let interrupted: NodeJS.Signals | undefined;
const interrupt = (signal: NodeJS.Signals): void => { interrupted = signal; };
process.once("SIGINT", () => interrupt("SIGINT"));
process.once("SIGTERM", () => interrupt("SIGTERM"));
process.once("SIGHUP", () => interrupt("SIGHUP"));
while (true) {
if (interrupted) throw new Error(`detached leader interrupted before release: ${interrupted}`);
const release = readDetachedLeaderReport(releasePath);
if (release?.kind === "release" && release.nonce === nonce && release.sessionId === payload.sessionId && release.sessionName === payload.sessionName && release.leaderPid === process.pid) {
detachedHudProof = parseDetachedHudAuthorityProof(release.hud);
break;
}
const abort = readDetachedLeaderReport(abortPath);
if (abort?.kind === "abort" && abort.nonce === nonce && abort.sessionId === payload.sessionId && abort.sessionName === payload.sessionName && abort.leaderPid === process.pid) {
throw new Error("detached launch aborted before release");
}
if (Date.now() >= releaseDeadline) throw new Error("detached leader release timed out");
blockMs(20);
}
rmSync(releasePath, { force: true });
rmSync(abortPath, { force: true });
}
const child = spawn(process.platform === "win32" ? "powershell.exe" : "/bin/sh", process.platform === "win32" ? ["-NoProfile", "-Command", payload.codexCmd] : ["-c", payload.codexCmd], {
cwd: payload.cwd,
stdio: "inherit",
...(process.platform === "win32" ? {} : { detached: true }),
});
let escalation: NodeJS.Timeout | undefined;
const signalChildTree = (signal: NodeJS.Signals, force = false): void => {
if (!child.pid) return;
try {
if (process.platform === "win32") execFileSync("taskkill", ["/PID", String(child.pid), "/T", ...(force ? ["/F"] : [])], { stdio: "ignore" });
else process.kill(-child.pid, signal);
} catch {}
};View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Check that the report directory is writable and not on a slow/stalled filesystem
- Look for a crashed or hung parent launcher process (check its logs) and retry
- Report a bug if the deadline is consistently too short for your environment (large sessions, cold tmux server)
Defensive patterns
Strategy: retry
Try / catch
try { await launchDetached(); } catch (e) {
if ((e as Error).message === 'detached leader release timed out') await sleep(2000), launchDetached(); // one retry
else throw e;
} Prevention
- Keep report directories on fast local filesystems
- Monitor the parent launcher for crashes
- Warm the tmux server before launching many sessions
When it happens
Trigger: Date.now() exceeds releaseDeadline without a valid release or abort report — launcher process hung, killed, or too slow to write the report file.
Common situations: Heavily loaded machine, tmux server startup latency, report file path permissions issues, or the parent CLI crashed before writing the report.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timed out waiting for tmux extended-keys lease lock: ${lockP
- detached Hermes leader has no tmux pane identity
- bound setup finalization denied: ${finalization.cleanup.comp
- detached leader interrupted before release: ${interrupted}
- detached launch aborted before release
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/7601738e4343f0dc.
Report an issue: GitHub.