Yeachan-Heo/oh-my-codex · critical · Error

detached ready report does not bind the leader pane

Error message

detached ready report does not bind the leader pane

What it means

The capturePane establishment phase re-reads the leader report and requires kind 'ready' with a paneId equal to the captured detachedLeaderPaneId and matching leaderPid. A mismatch means the ready report no longer describes the pane OMX intends to capture, so establishment aborts instead of capturing the wrong pane.

Solutions

  1. Clean stale detached state (release marker files, detached-active-record.json) and orphaned tmux sessions, then relaunch
  2. Avoid concurrent detached launches against the same cwd/session identity
  3. If it recurs, inspect the marker file's paneId/leaderPid versus the launched session (`tmux list-panes -a`) to find the writer
Defensive patterns

Strategy: retry

Validate before calling

const report = readDetachedLeaderReport(markerPath);
if (!(report?.kind === "ready" && report.paneId === detachedLeaderPaneId && report.leaderPid === pid)) await sleep(100); // leader may still be settling

Type guard

function bindsPane(r: any, paneId: string, pid: number): boolean { return r?.kind === "ready" && r.paneId === paneId && r.leaderPid === pid; }

Try / catch

try { establish(); } catch (e) { if (/does not bind the leader pane/.test(e.message)) { await cleanStaleDetachedState(); return establish(); } throw e; }

Prevention

When it happens

Trigger: The leader rewrote or rotated the release marker between the createInertSession and capturePane checks — e.g. leader restarted, multiple leaders racing, a stale marker from another launch, or tmux renumbering panes so pane ids diverge.

Common situations: Unclean shutdown leftovers making marker/session state inconsistent; rapid successive detached launches; leader process crash-and-retry loops republishing markers.

Related errors


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

Appendix: source

Thrown at src/cli/index.ts:6672

              if (report?.nonce === detachedLaunchNonce && report.sessionId === sessionId && report.sessionName === sessionName && report.leaderPid && report.kind === "failed") {
                detachedLeaderPid = report.leaderPid;
                return { kind: "failure", operation: "session-instructions", error: new Error(report.error || "detached leader setup failed") };
              }
              if (Date.now() >= readyDeadline) return { kind: "failure", operation: "session-instructions", error: new Error("detached leader readiness timed out") };
              blockMs(20);
            }
          },
          createInertSession: async () => {
            const report = readDetachedLeaderReport(releaseMarkerPath);
            if (report?.kind !== "ready" || report.nonce !== detachedLaunchNonce || report.sessionId !== sessionId || report.sessionName !== sessionName || report.leaderPid !== detachedLeaderPid) {
              throw new Error("detached ready report does not bind the inert session");
            }
            return sessionName;
          },
          capturePane: async () => {
            const report = readDetachedLeaderReport(releaseMarkerPath);
            if (!detachedLeaderPaneId || report?.kind !== "ready" || report.paneId !== detachedLeaderPaneId || report.leaderPid !== detachedLeaderPid) {
              throw new Error("detached ready report does not bind the leader pane");
            }
            return detachedLeaderPaneId;
          },
          updateNameMetadata: async () => {
            const state = await readSessionState(cwd, detachedSelectedStateEnv);
            if (state?.session_id !== sessionId || state.tmux_session_name !== sessionName) {
              throw new Error("detached session-name metadata was not committed by the leader");
            }
            return "committed-released";
          },
          updatePaneMetadata: async (_binding, pane) => {
            const state = await readSessionState(cwd, detachedSelectedStateEnv);
            if (state?.session_id !== sessionId || state.tmux_pane_id !== pane) {
              throw new Error("detached pane metadata was not committed by the leader");
            }
            return "committed-released";
          },
          publishActiveRecord: async () => {

View on GitHub (pinned to 3ad79a8a6f)