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

detached pane metadata was not committed by the leader

Error message

detached pane metadata was not committed by the leader

What it means

Same handshake family as 193 but for pane metadata: after the leader is told to commit the pane binding, OMX re-reads session state and requires session_id to match and tmux_pane_id to equal the expected pane. Otherwise the pane metadata commit is considered not performed and establishment fails.

Source

Thrown at src/cli/index.ts:6686

          },
          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 () => {
            const activeRecordPath = contextKey ? madmaxDetachedActiveRecordPath(runsRoot, contextKey) : join(omxRoot(cwd), "state", "detached-active-record.json");
            const record = readMadmaxDetachedActiveRecord(activeRecordPath);
            if (!record || record.launch_nonce !== detachedLaunchNonce || record.leader_pid !== detachedLeaderPid || record.session_id !== sessionId || record.tmux_session_name !== sessionName || record.tmux_pane_id !== detachedLeaderPaneId) throw new Error("detached active record does not bind the ready leader");
            const bytes = readFileSync(activeRecordPath, "utf-8");
            return { bytes, digest: createHash("sha256").update(bytes).digest("hex"), nonce: detachedLaunchNonce };
          },
          finalizeSetupFailure: async () => {},
          releaseBarrier: async () => {
            if (!detachedLeaderPid) throw new Error("detached leader PID missing before release");
            publishDetachedReleaseMarker(releaseMarkerPath, detachedLaunchNonce, sessionId, sessionName, detachedLeaderPid, detachedHudAuthority ?? undefined);
          },
          abortAndAwaitFinalization: async () => {
            // The leader has the retained binding. Publishing abort is the only
            // outer action permitted after a D9 write failure; a mismatched or

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Clean stale state and retry; ensure the OMX state dir is writable and fast (avoid NFS home dirs)
  2. Disable tmux hooks/plugins that create extra panes/windows on new sessions
  3. Serialize detached launches so state files are not shared
  4. Verify the leader command actually succeeded in tmux (attach and inspect, or check tmux capture-pane)
Defensive patterns

Strategy: retry

Validate before calling

const state = await readSessionState(cwd, env);
if (state?.session_id !== sessionId || state.tmux_pane_id !== pane) await sleep(50);

Type guard

function paneCommitted(s: any, sid: string, pane: string): boolean { return s?.session_id === sid && s?.tmux_pane_id === pane; }

Try / catch

try { establish(); } catch (e) { if (/pane metadata was not committed/.test(e.message)) { await cleanStaleState(); return establish(); } throw e; }

Prevention

When it happens

Trigger: The leader did not write tmux_pane_id for this session_id into session state in time (or wrote a different pane) — leader crash, state file contention, wrong env-selected state path, or tmux pane id churn from splits happening during bootstrap.

Common situations: Custom tmux configs that auto-split panes on session creation changing pane ids mid-handshake; stale state files; concurrent launches; slow writes racing the immediate re-read.

Related errors


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