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

tmux pane identity changed: ${normalizedLeaderPaneId}

Error message

tmux pane identity changed: ${normalizedLeaderPaneId}

What it means

Thrown by restoreStandaloneHudPane when the leader pane exists but its PID differs from options.expectedLeaderPanePid. This is an identity guard: tmux reuses pane ids (%N) after a server restart or pane churn, so matching the id alone is not proof it is the same pane. The library aborts rather than attach a HUD to a pane owned by a different process.

Source

Thrown at src/team/tmux-session.ts:3006

  leaderPaneId: string | null | undefined,
  cwd: string,
  options: RestoreStandaloneHudPaneOptions = {},
): string | null {
  const normalizedLeaderPaneId = normalizePaneTarget(leaderPaneId);
  if (!normalizedLeaderPaneId) return null;
  // The split is irreversible without a durable cleanup obligation. Validate
  // its canonical Team-root location before authorizing any pane effect.
  restoredHudCleanupDebtPath(cwd, options.stateRoot);

  const omxEntry = resolveOmxCliEntryPath();
  if (!omxEntry || omxEntry.trim() === '') return null;

  const leaderPanePid = (() => {
    const proof = readExactPaneProofSync(normalizedLeaderPaneId);
    if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);
    if (proof.status === 'gone') throw new Error(`tmux pane is not proven live: ${normalizedLeaderPaneId}`);
    if (options.expectedLeaderPanePid !== undefined && proof.pid !== options.expectedLeaderPanePid) {
      throw new Error(`tmux pane identity changed: ${normalizedLeaderPaneId}`);
    }
    return proof.pid;
  })();
  const requireAuthorizedLeaderPane = (): string => {
    options.assertLeaderPaneAuthorization?.();
    return requireLiveExactPaneSync(normalizedLeaderPaneId, options.expectedLeaderPanePid ?? leaderPanePid);
  };
  requireAuthorizedLeaderPane();

  const paneListResult = listPanesResult(normalizedLeaderPaneId);
  if (paneListResult.error) throw new Error(`failed to read tmux pane topology: ${paneListResult.error}`);
  const [existingHudPaneId, ...duplicateHudPaneIds] = findHudWatchPaneIds(
    paneListResult.panes,
    normalizedLeaderPaneId,
    { leaderPaneId: normalizedLeaderPaneId },
  );
  const ownedHudPanePids = new Map<string, number>();
  for (const paneId of [existingHudPaneId, ...duplicateHudPaneIds]) {

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Re-read the current pane PID (tmux display-message -p -t <pane> '#{pane_pid}') and pass that as expectedLeaderPanePid
  2. If the leader was legitimately restarted, update the persisted expected pid before restoring
  3. If tmux restarted, treat the old leader as gone and bootstrap a new session instead
  4. Ensure the pid and pane id are captured atomically from the same proof read

Example fix

// before
restoreStandaloneHudPane(leaderPaneId, cwd, { expectedLeaderPanePid: savedPid });
// after
const currentPid = Number(runTmux(['display-message','-p','-t',leaderPaneId,'#{pane_pid}']).stdout);
restoreStandaloneHudPane(leaderPaneId, cwd, { expectedLeaderPanePid: currentPid });
Defensive patterns

Strategy: try-catch

Validate before calling

const pid = Number(execSync(`tmux display-message -p -t ${leaderPaneId} '#{pane_pid}'`, {stdio:'pipe'}).toString().trim());
if (pid !== opts.expectedLeaderPanePid) throw new Error('refresh expectedLeaderPanePid before restore');

Try / catch

try { restoreStandaloneHudPane(leaderPaneId, cwd, { expectedLeaderPanePid }); } catch (e) { if (e instanceof Error && e.message.startsWith('tmux pane identity changed')) { const fresh = readCurrentPanePid(leaderPaneId); /* update persisted pid, retry or abort */ } else throw e; }

Prevention

When it happens

Trigger: restoreStandaloneHudPane called with expectedLeaderPanePid set, where readExactPaneProofSync(normalizedLeaderPaneId).pid !== expectedLeaderPanePid — tmux server restart recycled the pane id, the leader process was respawned, or the caller passed a mismatched pid/pane pair.

Common situations: tmux server restarted so %5 now belongs to a new process; agent/leader process crashed and was relaunched while stale expectedLeaderPanePid was persisted; concurrent sessions sharing pane ids; passing a pid captured from a different machine or container.

Related errors


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