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

  1. Find and remove the stale lock file at the path in the error message once no OMX process is running
  2. Reduce concurrency: serialize session launches so only one process configures tmux extended-keys at a time
  3. Check for orphaned omx/tmux processes (ps aux | grep omx) and kill them before retrying
  4. 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

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

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/e7e8b2f5c0575d6b. Report an issue: GitHub.