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

detached session-name metadata was not committed by the lead

Error message

detached session-name metadata was not committed by the leader

What it means

After instructing the leader to commit session-name metadata, OMX re-reads session state and requires session_id and tmux_session_name to match the current launch. A mismatch means the leader never committed (or committed different) session-name metadata, so the establishment handshake fails.

Source

Thrown at src/cli/index.ts:6679

          },
          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 () => {
            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 () => {},

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Delete stale session state files under the OMX root state directory and retry the launch
  2. Ensure the launch env (CODEX_HOME, OMX root vars) is consistent between the leader spawn and the validation step
  3. Rule out concurrent OMX sessions writing the same state file; serialize launches
  4. Check the leader process is alive and healthy during bootstrap (tmux session not dying instantly due to a failing command)
Defensive patterns

Strategy: retry

Validate before calling

const state = await readSessionState(cwd, env);
if (state?.session_id !== sessionId || state.tmux_session_name !== sessionName) await sleep(50); // allow leader write to land

Type guard

function stateMatches(s: any, sid: string, name: string): boolean { return s?.session_id === sid && s?.tmux_session_name === name; }

Try / catch

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

Prevention

When it happens

Trigger: readSessionState returns state whose session_id differs or whose tmux_session_name does not match — the leader process died before writing, wrote to a different state file (env-dependent selection via detachedSelectedStateEnv), or a stale state file from a previous session shadows the new one.

Common situations: Leftover state files from crashed sessions; env differences (CODEX_HOME/OMX root overrides) pointing the leader and validator at different state paths; slow disks where the leader write lands after validation; concurrent sessions overwriting shared state.

Related errors


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