Yeachan-Heo/oh-my-codex · critical · Error
tmux pane identity changed: ${target}
Error message
tmux pane identity changed: ${target} What it means
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.
Source
Thrown at src/team/tmux-session.ts:306
if (!/^%[0-9]+$/.test(target)) throw new Error(`invalid tmux source pane: ${paneId}`);
const result = runTmuxStructured([
'display-message', '-p', '-t', target,
'#{session_name}\t#{session_id}\t#{session_created}\t#{window_index}\t#{window_id}\t#{pane_id}\t#{pane_pid}',
]);
if (!result.ok) throw new Error(`failed to capture tmux source authority: ${result.stderr}`);
const fields = result.stdout.split('\t');
if (fields.length !== 7) throw new Error('malformed tmux source authority');
const [sessionName, sessionId, sessionCreated, windowIndex, windowId, capturedPaneId, panePid] = fields;
if (!sessionName || !/^\$[0-9]+$/.test(sessionId) || !/^[0-9]+$/.test(sessionCreated)
|| !/^[0-9]+$/.test(windowIndex) || !/^@[0-9]+$/.test(windowId)
|| capturedPaneId !== target || !/^[1-9][0-9]*$/.test(panePid)) {
throw new Error('malformed tmux source authority');
}
const parsedPid = Number(panePid);
if (!Number.isSafeInteger(parsedPid) || parsedPid <= 0) throw new Error('malformed tmux source authority');
const proof = readExactPaneProofSync(target);
if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);
if (proof.status !== 'live' || proof.pid !== parsedPid) throw new Error(`tmux pane identity changed: ${target}`);
const ownerResult = runTmuxStructured(['show-option', '-qv', '-p', '-t', target, OMX_TEAM_PANE_OWNER_OPTION]);
if (!ownerResult.ok) throw new Error(`failed to capture tmux pane owner: ${ownerResult.stderr}`);
const teamPaneOwnerId = ownerResult.stdout.trim() || null;
if (teamPaneOwnerId && !/^[A-Za-z0-9._:-]+$/.test(teamPaneOwnerId)) {
throw new Error('malformed tmux pane owner');
}
if (expectedTeamPaneOwnerId !== undefined && teamPaneOwnerId !== expectedTeamPaneOwnerId) {
throw new Error(`tmux pane team owner changed: ${target}`);
}
return {
paneId: target,
panePid: parsedPid,
sessionName,
sessionId,
sessionCreated,
windowId,
windowIndex,
teamPaneOwnerId,View on GitHub (pinned to 3ad79a8a6f)
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
Example fix
// before
const source = captureSourcePaneAuthority(paneId);
// after
const source = retryStale(() => captureSourcePaneAuthority(paneId),
(e) => e.message.startsWith('tmux pane identity changed')); Defensive patterns
Strategy: retry
Try / catch
catch (e) { if (/^tmux pane identity changed/.test(e.message)) return recaptureAndRetry(paneId, 2); throw e; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- tmux source authority changed before effect
- tmux pane identity changed: ${paneId}
- invalid auth slot path
- ${label} is not a file: ${path}
- Refusing to overwrite existing ${repoRelative(cwd, missionPa
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/82f7eadd3875e521.
Report an issue: GitHub.