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

detached session did not report a leader pane id

Error message

detached session did not report a leader pane id

What it means

During detached bootstrap, the `tmux new-session` step's output could not be parsed into a leader pane id. OMX relies on that pane id as the authority anchor for all subsequent owner-guarded tmux mutations, so an unparseable output is fatal rather than retried.

Source

Thrown at src/cli/index.ts:6588

          hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId,
          projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, codexEnvWithNotify,
          sqliteHomeOverride, detachedParentEnvFilePath, inheritedWorkerModel, releaseMarkerPath,
          omxBin,
          {
            ...(preLaunchOptions.notifyTempContract.active ? { notifyTempContract: preLaunchOptions.notifyTempContract } : {}),
            enableNotifyFallbackAuthority: preLaunchOptions.enableNotifyFallbackAuthority,
            worktreeDirty: preLaunchOptions.worktreeDirty,
            shouldAttach: detachedPreflight.shouldAttach,
          },
          detachedControlPlane,
        );
        for (const step of bootstrapSteps) {
          try {
            if (step.name === "new-session") {
              const output = execTmuxFileSync(step.args, { stdio: "pipe", encoding: "utf-8" });
              createdSession = true;
              const leaderPaneId = parsePaneIdFromTmuxOutput(output || "");
              if (!leaderPaneId) throw new Error("detached session did not report a leader pane id");
              detachedLeaderPaneId = leaderPaneId;
              detachedLeaderAuthority = captureDetachedLeaderAuthority(leaderPaneId, sessionName, sessionId);
              continue;
            }
            const authority = detachedLeaderAuthority;
            if (!authority) throw new Error("detached leader authority missing before tmux mutation");
            if (step.name === "tag-session") {
              runDetachedLeaderMutation(authority, step.args, false);
              // tmux can acknowledge the session tag before the first owner-format
              // evaluation sees it. Retrying this idempotent first owner-guarded
              // mutation once gives the server a command boundary without relaxing
              // any part of the captured session, window, pane, PID, or owner fence.
              runDetachedLeaderMutation(authority, ["set-option", "-q", "-t", authority.sessionName, "history-limit", String(DETACHED_TMUX_HISTORY_LIMIT)], true, true);
              runDetachedLeaderMutation(authority, ["set-option", "-pq", "-t", authority.paneId, "history-limit", String(DETACHED_TMUX_HISTORY_LIMIT)]);
              // #3266: the owned leader pane must close with its process so a normal
              // child exit destroys the session naturally even when the user's tmux
              // config inherits remain-on-exit=on/failed.
              runDetachedLeaderMutation(authority, ["set-option", "-pq", "-t", authority.paneId, "remain-on-exit", "off"]);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Test with a clean config: `tmux -f /dev/null new-session -d` style check, or launch OMX with TMUX_CONF overrides to rule out config interference
  2. Upgrade tmux to a modern release (3.2+) so new-session output matches the expected format
  3. Audit tmux hooks/plugins in ~/.tmux.conf that write to stdout during session creation and silence them
  4. Report the exact tmux version/output to OMX maintainers if it persists on a clean config

Example fix

# before (~/.tmux.conf)
set-hook -g session-created 'run-shell "echo created"'

# after
set-hook -g session-created 'run-shell -b "echo created >/dev/null"'
Defensive patterns

Strategy: validation

Validate before calling

const out = execTmuxFileSync(["new-session", "-d", "-P", "-F", "#{pane_id}", ...], { stdio: "pipe" });
if (!/^%\d+$/.test((out || "").trim())) { /* abort before authority-dependent steps */ }

Type guard

function isPaneId(v: string): boolean { return /^%\d+$/.test(v.trim()); }

Try / catch

try { bootstrap(); } catch (e) { if (e.message === "detached session did not report a leader pane id") { /* test with tmux -f /dev/null; audit hooks */ } throw e; }

Prevention

When it happens

Trigger: execTmuxFileSync for the new-session step emits no parseable pane id — because a tmux format/display option in the user's ~/.tmux.conf alters new-session output, an ancient tmux version formats output differently, or tmux printed a warning line instead of the id.

Common situations: Custom tmux configs with hooks (hook-attached-session, etc.) that print to stdout; tmux < 3.x with different formatting; tmux banners/messages from plugins like tmux-continuum appearing on session creation.

Related errors


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