Yeachan-Heo/oh-my-codex · error · Error
failed to read tmux pane topology before split: ${beforeTopo
Error message
failed to read tmux pane topology before split: ${beforeTopology.error} What it means
Before performing a source-authorized split, the code snapshots the pane topology of the source's window (via listPanesResult on the immutable windowId). If that snapshot read fails, the split is aborted because the post-split reconciliation would be unable to distinguish new panes from pre-existing ones.
Source
Thrown at src/team/tmux-session.ts:388
`${effect} ; display-message -p ${shellQuoteSingle(receipt)}`,
"display-message -p ''",
]);
if (!result.ok) throw new Error(`tmux source authority transaction failed: ${result.stderr}`);
if (result.stdout !== receipt) throw new Error('tmux source authority changed before effect');
return receipt;
}
export function runSourceAuthorizedSplit(
source: SourcePaneAuthority,
buildEffect: (receipt: string) => string,
): string {
// Reconcile against the immutable window ID, not the mutable session:index
// target: a concurrent renumber between the guarded split and the after-read
// must never turn an unrelated window's pane into kill authority.
const windowTarget = source.windowId;
const beforeTopology = listPanesResult(windowTarget);
if (beforeTopology.error) {
throw new Error(`failed to read tmux pane topology before split: ${beforeTopology.error}`);
}
const beforePaneIds = new Set(beforeTopology.panes.map((pane) => pane.paneId));
const receipt = sourceTransactionReceipt();
const effect = buildEffect(receipt);
const result = runTmux([
'if-shell', '-F', '-t', source.paneId, sourceAuthorityPredicate(source),
bindSplitWindowReceiptFormat(effect, receipt),
"display-message -p ''",
], true);
if (!result.ok) throw new Error(`tmux source authority transaction failed: ${result.stderr}`);
const paneId = parseSplitWindowPaneId(result.stdout, receipt);
if (!paneId) {
const afterTopology = listPanesResult(windowTarget);
const newlyObservedPaneIds = afterTopology.error
? []
: afterTopology.panes
.filter((pane) => !beforePaneIds.has(pane.paneId) && pane.startCommand.includes(receipt))
.map((pane) => pane.paneId);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Check the embedded error: window gone → re-create session; transient → retry split from a fresh authority capture
- Verify the window still exists: `tmux list-panes -t @<windowId>`
- Pin/verify a tmux version compatible with the format strings used
Defensive patterns
Strategy: retry
Validate before calling
const t = listPanesResult(windowId); if (t.error) throw new Error(`window ${windowId} unreadable: ${t.error}`); Try / catch
catch (e) { if (/topology before split/.test(e.message)) return retryFromFreshCapture(); throw e; } Prevention
- Verify the window exists before splitting
- Pin a known-good tmux version in CI and docs
When it happens
Trigger: runSourceAuthorizedSplit (via guardedSplitPaneId or createTeamSession) when `tmux list-panes -t <windowId>` fails: window closed, session killed, server error, or format-string incompatibility with the installed tmux version.
Common situations: The window/session is torn down concurrently with a split; tmux version differences in list-panes format specifiers; server socket flakiness under heavy load.
Related errors
- invalid auth slot path
- auth directory must not be a symlink: ${dir}
- auth path is not a directory: ${dir}
- ${label} is not a file: ${path}
- ${label} not found: ${path}
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/cd07eb8222b6bcae.
Report an issue: GitHub.