Yeachan-Heo/oh-my-codex · error · Error
timed out waiting for tmux extended-keys lease lock: ${lockP
Error message
timed out waiting for tmux extended-keys lease lock: ${lockPath} What it means
A inter-process lock file used to serialize tmux extended-keys configuration changes could not be acquired within the retry budget. The code loops retrying the lock with a fixed sleep (TMUX_EXTENDED_KEYS_LOCK_RETRY_MS) and a timeout, then throws with the lock path.
Source
Thrown at src/cli/index.ts:4872
let holderAlive = false;
try {
const holderPid = Number.parseInt(readFileSync(join(lockPath, "pid"), "utf-8").trim(), 10);
if (Number.isFinite(holderPid) && holderPid > 0) {
process.kill(holderPid, 0);
holderAlive = true;
}
} catch {
// PID file missing/unreadable or process dead (ESRCH) — treat as stale
}
if (!holderAlive) {
rmSync(lockPath, { recursive: true, force: true });
continue;
}
}
blockMs(TMUX_EXTENDED_KEYS_LOCK_RETRY_MS);
}
}
throw new Error(`timed out waiting for tmux extended-keys lease lock: ${lockPath}`);
}
interface DetachedLeaderPreLaunchOptions {
notifyTempContract?: NotifyTempContract;
enableNotifyFallbackAuthority: boolean;
worktreeDirty: boolean;
shouldAttach?: boolean;
}
function buildDetachedSessionLeaderCommand(
cwd: string,
sessionName: string,
codexCmd: string,
sessionId: string | undefined,
codexHomeOverride: string | undefined,
projectLocalCodexHomeForCleanup: string | undefined,
runtimeCodexHomeForCleanup: string | undefined,
parentEnvFilePath: string | undefined,View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Find and remove the stale lock file at the path in the error message once no OMX process is running
- Reduce concurrency: serialize session launches so only one process configures tmux extended-keys at a time
- Check for orphaned omx/tmux processes (ps aux | grep omx) and kill them before retrying
- Retry the command after the timeout — transient contention resolves itself when the holder exits
Example fix
// before
await Promise.all(Array.from({length: 5}, () => startSession()));
// after
for (const job of jobs) { await startSession(); } // serialize to avoid lock contention Defensive patterns
Strategy: retry
Validate before calling
const holder = tryAcquireLock(lockPath, { staleMs: 30_000 });
if (!holder) { /* skip extended-keys config this run or wait */ } Try / catch
try { configureExtendedKeys(); } catch (e) { if (e instanceof Error && e.message.includes("lease lock")) { await sleep(5_000); return configureExtendedKeys(); /* bounded retry */ } throw e; } Prevention
- Serialize concurrent OMX launches in scripts (queue or limit=1)
- Remove stale lock files after killing OMX processes
- Treat lock timeout as transient: retry before treating as fatal
When it happens
Trigger: Multiple OMX invocations (e.g. concurrent sessions, a launch storm, or a stuck previous process holding the lock file) contend for the tmux extended-keys lease lock; a stale lock from a killed process with no cleanup, or many parallel detached launches, exhausts the timeout.
Common situations: Scripts that launch several OMX sessions concurrently; a previously SIGKILLed process leaving a stale lock file in state/; slow or hung filesystems (NFS) making the lock appear held; CI environments running parallel matrix jobs sharing a home dir.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- [native-assets] publication-lock-timeout: ${path}; elapsed=$
- Failed to acquire AGENTS.md lock within timeout
- shutdown_shared_session_${kind.replaceAll(' ', '_')}_owner_c
- Timed out acquiring task claim lock for ${teamName}/${taskId
- Timed out waiting for ultragoal mutation lock at ${repoRelat
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/e7e8b2f5c0575d6b.
Report an issue: GitHub.