{"record":{"id":"82f7eadd3875e521","repo":"Yeachan-Heo/oh-my-codex","slug":"tmux-pane-identity-changed-target","errorCode":null,"errorMessage":"tmux pane identity changed: ${target}","messagePattern":"tmux pane identity changed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/team/tmux-session.ts","lineNumber":306,"sourceCode":"  if (!/^%[0-9]+$/.test(target)) throw new Error(`invalid tmux source pane: ${paneId}`);\n  const result = runTmuxStructured([\n    'display-message', '-p', '-t', target,\n    '#{session_name}\\t#{session_id}\\t#{session_created}\\t#{window_index}\\t#{window_id}\\t#{pane_id}\\t#{pane_pid}',\n  ]);\n  if (!result.ok) throw new Error(`failed to capture tmux source authority: ${result.stderr}`);\n  const fields = result.stdout.split('\\t');\n  if (fields.length !== 7) throw new Error('malformed tmux source authority');\n  const [sessionName, sessionId, sessionCreated, windowIndex, windowId, capturedPaneId, panePid] = fields;\n  if (!sessionName || !/^\\$[0-9]+$/.test(sessionId) || !/^[0-9]+$/.test(sessionCreated)\n    || !/^[0-9]+$/.test(windowIndex) || !/^@[0-9]+$/.test(windowId)\n    || capturedPaneId !== target || !/^[1-9][0-9]*$/.test(panePid)) {\n    throw new Error('malformed tmux source authority');\n  }\n  const parsedPid = Number(panePid);\n  if (!Number.isSafeInteger(parsedPid) || parsedPid <= 0) throw new Error('malformed tmux source authority');\n  const proof = readExactPaneProofSync(target);\n  if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);\n  if (proof.status !== 'live' || proof.pid !== parsedPid) throw new Error(`tmux pane identity changed: ${target}`);\n  const ownerResult = runTmuxStructured(['show-option', '-qv', '-p', '-t', target, OMX_TEAM_PANE_OWNER_OPTION]);\n  if (!ownerResult.ok) throw new Error(`failed to capture tmux pane owner: ${ownerResult.stderr}`);\n  const teamPaneOwnerId = ownerResult.stdout.trim() || null;\n  if (teamPaneOwnerId && !/^[A-Za-z0-9._:-]+$/.test(teamPaneOwnerId)) {\n    throw new Error('malformed tmux pane owner');\n  }\n  if (expectedTeamPaneOwnerId !== undefined && teamPaneOwnerId !== expectedTeamPaneOwnerId) {\n    throw new Error(`tmux pane team owner changed: ${target}`);\n  }\n  return {\n    paneId: target,\n    panePid: parsedPid,\n    sessionName,\n    sessionId,\n    sessionCreated,\n    windowId,\n    windowIndex,\n    teamPaneOwnerId,","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/tmux-session.ts#L288-L324","documentation":"The pane ID still exists, but the process behind it (pane_pid from display-message) does not match the PID proved by a fresh kernel-level snapshot via readExactPaneProofSync, or the proof says the pane is not live. tmux reuses pane IDs after kill+respawn, so a PID mismatch means the pane was destroyed and a different pane now owns the same %id — acting on it would hit the wrong process.","triggerScenarios":"Between `display-message` and `readExactPaneProofSync`, the pane exited and tmux respawned it (remain-on-exit/respawn behaviour), the pane was killed by another client, or its process was replaced. Any captureSourcePaneAuthority call can hit this under concurrency.","commonSituations":"An agent worker pane crashed and tmux respawn-pane recycled the ID; scripted automation killed panes concurrently with team-session creation; a shell startup hook (respawn) races the capture.","solutions":["Treat the captured authority as stale: re-read the current pane ID/PID and retry capture","Stop anything that respawns or kills panes concurrently while creating/authorizing team sessions","If you hold the pane PID, verify the process identity before retrying so you do not authorize the recycled pane"],"exampleFix":"// before\nconst source = captureSourcePaneAuthority(paneId);\n// after\nconst source = retryStale(() => captureSourcePaneAuthority(paneId),\n  (e) => e.message.startsWith('tmux pane identity changed'));","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (e) { if (/^tmux pane identity changed/.test(e.message)) return recaptureAndRetry(paneId, 2); throw e; }","preventionTips":["Disable respawn-on-exit for managed panes","Freeze (paneId, panePid) pairs and re-verify both before any effect","Serialize pane mutations; avoid concurrent clients killing/respawning panes"],"tags":["tmux","pid-recycling","race-condition","security"],"backgroundTag":"stale-resource-handle","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}