{"record":{"id":"a4b00c49072ca21c","repo":"Yeachan-Heo/oh-my-codex","slug":"tmux-pane-identity-changed-normalizedleaderpane","errorCode":null,"errorMessage":"tmux pane identity changed: ${normalizedLeaderPaneId}","messagePattern":"tmux pane identity changed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/tmux-session.ts","lineNumber":3006,"sourceCode":"  leaderPaneId: string | null | undefined,\n  cwd: string,\n  options: RestoreStandaloneHudPaneOptions = {},\n): string | null {\n  const normalizedLeaderPaneId = normalizePaneTarget(leaderPaneId);\n  if (!normalizedLeaderPaneId) return null;\n  // The split is irreversible without a durable cleanup obligation. Validate\n  // its canonical Team-root location before authorizing any pane effect.\n  restoredHudCleanupDebtPath(cwd, options.stateRoot);\n\n  const omxEntry = resolveOmxCliEntryPath();\n  if (!omxEntry || omxEntry.trim() === '') return null;\n\n  const leaderPanePid = (() => {\n    const proof = readExactPaneProofSync(normalizedLeaderPaneId);\n    if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);\n    if (proof.status === 'gone') throw new Error(`tmux pane is not proven live: ${normalizedLeaderPaneId}`);\n    if (options.expectedLeaderPanePid !== undefined && proof.pid !== options.expectedLeaderPanePid) {\n      throw new Error(`tmux pane identity changed: ${normalizedLeaderPaneId}`);\n    }\n    return proof.pid;\n  })();\n  const requireAuthorizedLeaderPane = (): string => {\n    options.assertLeaderPaneAuthorization?.();\n    return requireLiveExactPaneSync(normalizedLeaderPaneId, options.expectedLeaderPanePid ?? leaderPanePid);\n  };\n  requireAuthorizedLeaderPane();\n\n  const paneListResult = listPanesResult(normalizedLeaderPaneId);\n  if (paneListResult.error) throw new Error(`failed to read tmux pane topology: ${paneListResult.error}`);\n  const [existingHudPaneId, ...duplicateHudPaneIds] = findHudWatchPaneIds(\n    paneListResult.panes,\n    normalizedLeaderPaneId,\n    { leaderPaneId: normalizedLeaderPaneId },\n  );\n  const ownedHudPanePids = new Map<string, number>();\n  for (const paneId of [existingHudPaneId, ...duplicateHudPaneIds]) {","sourceCodeStart":2988,"sourceCodeEnd":3024,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/tmux-session.ts#L2988-L3024","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-read the current pane PID (tmux display-message -p -t <pane> '#{pane_pid}') and pass that as expectedLeaderPanePid","If the leader was legitimately restarted, update the persisted expected pid before restoring","If tmux restarted, treat the old leader as gone and bootstrap a new session instead","Ensure the pid and pane id are captured atomically from the same proof read"],"exampleFix":"// before\nrestoreStandaloneHudPane(leaderPaneId, cwd, { expectedLeaderPanePid: savedPid });\n// after\nconst currentPid = Number(runTmux(['display-message','-p','-t',leaderPaneId,'#{pane_pid}']).stdout);\nrestoreStandaloneHudPane(leaderPaneId, cwd, { expectedLeaderPanePid: currentPid });","handlingStrategy":"try-catch","validationCode":"const pid = Number(execSync(`tmux display-message -p -t ${leaderPaneId} '#{pane_pid}'`, {stdio:'pipe'}).toString().trim());\nif (pid !== opts.expectedLeaderPanePid) throw new Error('refresh expectedLeaderPanePid before restore');","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Persist pane_id and pane_pid together from a single proof read","On leader restart, update the stored expected pid before any restore","Consider tmux server restarts as invalidating all (pane_id, pid) pairs"],"tags":["tmux","pane-identity","pid-mismatch","server-restart"],"backgroundTag":"stale-resource-handle","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}