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

detached active record does not bind the ready leader

Error message

detached active record does not bind the ready leader

What it means

Before publishing, OMX reads the detached active record (per-context or default state/detached-active-record.json) and requires every identity field — launch_nonce, leader_pid, session_id, tmux_session_name, tmux_pane_id — to match the ready leader. Any missing record or mismatched field aborts, since publishing a record that binds the wrong session would misroute later clients.

Source

Thrown at src/cli/index.ts:6693

          },
          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
            // missing report leaves every artifact and the tmux session intact.
            if (!detachedLeaderPid) {
              rollbackFromPreReportAuthority = detachedLeaderAuthority !== null;
              return { acknowledged: false, rollbackAuthorized: rollbackFromPreReportAuthority };
            }
            const current = readDetachedLeaderReport(releaseMarkerPath);
            if (current?.nonce === detachedLaunchNonce && current.sessionId === sessionId &&

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Remove state/detached-active-record.json (and any context-keyed variants) plus release markers, then relaunch
  2. Ensure contextKey/runsRoot resolution is identical between the leader environment and the outer launch (consistent env vars)
  3. Serialize detached launches per context
  4. If persistent, dump the record file and compare each field against the current launch values to find the divergent writer
Defensive patterns

Strategy: fallback

Validate before calling

const record = readMadmaxDetachedActiveRecord(activeRecordPath);
const binds = record && record.launch_nonce === nonce && record.leader_pid === pid && record.session_id === sid && record.tmux_session_name === name && record.tmux_pane_id === pane;
if (!binds) await cleanStaleDetachedState();

Type guard

function recordBindsLeader(r: any, l: { nonce: string; pid: number; sid: string; name: string; pane: string }): boolean {
  return !!r && r.launch_nonce === l.nonce && r.leader_pid === l.pid && r.session_id === l.sid && r.tmux_session_name === l.name && r.tmux_pane_id === l.pane;
}

Try / catch

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

Prevention

When it happens

Trigger: readMadmaxDetachedActiveRecord returns null (leader never wrote the record) or a record with any field diverging from the current launch — stale record from a prior launch, leader crash before publishing, context key selecting a different record path, or split/racing processes overwriting the record.

Common situations: Relaunching after crashes without cleaning state; madmax/multi-context setups where contextKey resolution differs between leader and validator; concurrent detached launches; disk issues dropping the leader's write.

Related errors


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