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

detached Hermes leader has no tmux pane identity

Error message

detached Hermes leader has no tmux pane identity

What it means

For a shouldAttach===false detached leader, the process must already live inside a tmux pane identified by TMUX_PANE matching ^%[0-9]+$, otherwise it has no pane identity to bind to.

Source

Thrown at src/cli/index.ts:7029

function traceDetachedLeaderPhase(phase: string): void {
  if (process.env.OMX_TEST_DETACHED_TRACE !== "1") return;
  const line = `[omx-test-detached] ${phase}\n`;
  process.stderr.write(line);
  const tracePath = process.env.OMX_TEST_DETACHED_TRACE_PATH;
  if (tracePath) appendFileSync(tracePath, line);
}

async function runDetachedSessionLeader(payload: DetachedLeaderPayload): Promise<void> {
  for (const [key, value] of Object.entries(payload.parentEnv ?? {})) {
    if (isDetachedLaunchControlPlaneKey(key, process.platform === "win32")) continue;
    process.env[key] = value;
  }
  let pane: string;
  const inheritedPane = process.env.TMUX_PANE?.trim();
  if (payload.preLaunchOptions.shouldAttach === false) {
    if (!inheritedPane || !/^%[0-9]+$/.test(inheritedPane)) {
      throw new Error("detached Hermes leader has no tmux pane identity");
    }
    pane = inheritedPane;
  } else {
    const paneSnapshot = execTmuxFileSync(
      ["list-panes", "-t", payload.sessionName, "-F", "#{pane_id}\t#{pane_dead}\t#{pane_pid}"],
      { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] },
    );
    pane = parseDetachedLeaderPaneIdByPid(paneSnapshot, process.pid);
  }
  process.env.TMUX_PANE = pane;
  const established = await establishPreLaunchBinding(payload.cwd, payload.sessionId, {
    tmuxSessionName: payload.sessionName,
    tmuxPaneId: pane,
  });
  const binding = established.binding;
  let ownedRecord: DetachedActiveRecordOwnership | undefined;
  let detachedHudProof: DetachedHudAuthority | undefined;
  const nonce = payload.readyPath?.split(".").at(-2) || payload.sessionId;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Launch through the parent CLI's detached-session flow so the leader inherits TMUX_PANE from the created pane
  2. If running manually, do it inside the target tmux session: tmux new-session -d -s mysession 'leader-cmd'
  3. Validate process.env.TMUX_PANE matches /^%[0-9]+$/ before starting the leader

Example fix

// before
$ hermes detached-leader --no-attach   # from a bare shell
// after
$ tmux new-session -d -s mysession 'hermes detached-leader --no-attach'
Defensive patterns

Strategy: validation

Validate before calling

if (options.shouldAttach === false && !/^%[0-9]+$/.test(process.env.TMUX_PANE ?? '')) {
  throw new Error('must run inside a tmux pane (TMUX_PANE unset)');
}

Type guard

function hasTmuxPaneIdentity(): boolean {
  return /^%[0-9]+$/.test(process.env.TMUX_PANE?.trim() ?? '');
}

Prevention

When it happens

Trigger: Running the detached leader binary directly from a plain shell (no tmux), TMUX_PANE unset/empty, or set to a malformed value (e.g. '0' or '%abc').

Common situations: User invokes the internal leader command manually to debug, or a wrapper (nohup, setsid, systemd) strips TMUX_* variables. Also occurs when the pane was destroyed between launch and leader start.

Related errors


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