{"record":{"id":"1bd6b9fc7dafa353","repo":"Yeachan-Heo/oh-my-codex","slug":"tmux-source-authority-changed-before-effect","errorCode":null,"errorMessage":"tmux source authority changed before effect","messagePattern":"tmux source authority changed before effect","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/team/tmux-session.ts","lineNumber":374,"sourceCode":"  if (firstFormatIndex < 0 || effect.indexOf(SPLIT_WINDOW_PANE_FORMAT, firstFormatIndex + SPLIT_WINDOW_PANE_FORMAT.length) >= 0) {\n    throw new Error('tmux_split_window_format_contract_invalid');\n  }\n  return `${effect.slice(0, firstFormatIndex)}-F '#{pane_id}:${receipt}'${effect.slice(firstFormatIndex + SPLIT_WINDOW_PANE_FORMAT.length)}`;\n}\n\nfunction bindSplitReceiptToPaneCommand(command: string, receipt: string): string {\n  return `${command} # ${receipt}`;\n}\n\n/** Queue an effect in the source pane's tmux server only when its exact pane/session/window incarnation still matches. */\nexport function runSourceAuthorizedTmux(source: SourcePaneAuthority, effect: string, receipt: string = sourceTransactionReceipt()): string {\n  const result = runTmux([\n    'if-shell', '-F', '-t', source.paneId, sourceAuthorityPredicate(source),\n    `${effect} ; display-message -p ${shellQuoteSingle(receipt)}`,\n    \"display-message -p ''\",\n  ]);\n  if (!result.ok) throw new Error(`tmux source authority transaction failed: ${result.stderr}`);\n  if (result.stdout !== receipt) throw new Error('tmux source authority changed before effect');\n  return receipt;\n}\n\nexport function runSourceAuthorizedSplit(\n  source: SourcePaneAuthority,\n  buildEffect: (receipt: string) => string,\n): string {\n  // Reconcile against the immutable window ID, not the mutable session:index\n  // target: a concurrent renumber between the guarded split and the after-read\n  // must never turn an unrelated window's pane into kill authority.\n  const windowTarget = source.windowId;\n  const beforeTopology = listPanesResult(windowTarget);\n  if (beforeTopology.error) {\n    throw new Error(`failed to read tmux pane topology before split: ${beforeTopology.error}`);\n  }\n  const beforePaneIds = new Set(beforeTopology.panes.map((pane) => pane.paneId));\n  const receipt = sourceTransactionReceipt();\n  const effect = buildEffect(receipt);","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/tmux-session.ts#L356-L392","documentation":"The if-shell authority predicate evaluated false for the source pane, so the effect branch did not run and tmux printed an empty receipt instead of the expected transaction receipt. This is the deliberate abort path: the pane's identity/session/owner changed between capture and effect, and the library refused to act on stale authority.","triggerScenarios":"Between captureSourcePaneAuthority and runSourceAuthorizedTmux, the pane was respawned (PID changed), the session/window incarnation changed, or the team owner option was rewritten — so sourceAuthorityPredicate(source) no longer matches.","commonSituations":"Concurrent pane kills/respawns during team layout; another omx instance re-tagged the pane; user manually respawned panes while a session was being created.","solutions":["Re-capture the source authority and retry the effect against the fresh snapshot","Serialize team layout operations so nothing mutates the source pane mid-transaction","Investigate what is respawning/retagging panes (hooks, other agents) and stop it"],"exampleFix":"// before\nrunSourceAuthorizedTmux(source, effect);\n// after\nconst fresh = captureSourcePaneAuthority(source.paneId, source.teamPaneOwnerId);\nrunSourceAuthorizedTmux(fresh, effect);","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"catch (e) { if (/changed before effect/.test(e.message)) { const fresh = captureSourcePaneAuthority(paneId); return redo(fresh); } throw e; }","preventionTips":["Treat this as an optimistic-concurrency abort: always re-capture then retry","Freeze concurrent pane mutations (respawn/kill) during transactions"],"tags":["tmux","race-condition","transaction","security"],"backgroundTag":"optimistic-concurrency-conflict","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}