Yeachan-Heo/oh-my-codex · warning · Error

tmux pane is not proven live: ${paneId}

Error message

tmux pane is not proven live: ${paneId}

What it means

requireLiveExactPaneSync demands a fresh global snapshot proving the pane is live before any destructive operation. `gone' means tmux explicitly reported the pane as dead/deleted — callers should treat this as 'already cleaned' rather than an actual failure, but the guard throws so no action is taken.

Source

Thrown at src/team/tmux-session.ts:585

  const hudMatches = !topology.error && topology.panes.filter((pane) => pane.paneId === debt.pane_id
    && hudPaneMatchesOwner(pane, { leaderPaneId: debt.hud_owner_leader_pane_id })).length === 1;
  if (!hudMatches) throw new Error(`restored_hud_cleanup_debt_unresolved:${debt.pane_id}`);
  const finalProof = readExactPaneProofSync(debt.pane_id);
  if (finalProof.status !== 'live' || finalProof.pid !== debt.pane_pid) {
    throw new Error(`restored_hud_cleanup_debt_unresolved:${debt.pane_id}`);
  }
  removeRestoredHudCleanupDebtSync(record.path);
}


/**
 * Kill only a pane that a fresh global snapshot proves live. Callers treat
 * gone/dead rows as already cleaned and unavailable snapshots as fail-closed.
 */
function requireLiveExactPaneSync(paneId: string, expectedPid?: number): string {
  const proof = readExactPaneProofSync(paneId);
  if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);
  if (proof.status === 'gone') throw new Error(`tmux pane is not proven live: ${paneId}`);
  if (expectedPid !== undefined && proof.pid !== expectedPid) {
    throw new Error(`tmux pane identity changed: ${paneId}`);
  }
  return proof.paneId;
}

function killExactPaneSync(paneId: string, expectedPid?: number, assertAuthorization?: () => void): void {
  const proof = readExactPaneProofSync(paneId);
  if (proof.status === 'unavailable') throw new ExactPaneProofUnavailableError(proof);
  if (proof.status === 'gone') return;
  if (expectedPid !== undefined && proof.pid !== expectedPid) {
    throw new Error(`tmux pane identity changed: ${paneId}`);
  }
  assertAuthorization?.();
  // Authorization can read tmux. Re-prove immediately after it so a recycled
  // pane ID cannot be targeted by the subsequent kill.
  const finalProof = readExactPaneProofSync(proof.paneId);
  if (finalProof.status === 'unavailable') throw new ExactPaneProofUnavailableError(finalProof);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Catch this and treat as success/no-op when cleaning up (pane already gone)
  2. Avoid retry storms: check whether your orchestrator already killed the pane
  3. No repair needed if the goal was removal

Example fix

// before
requireLiveExactPaneSync(paneId);
// after
try { requireLiveExactPaneSync(paneId); }
catch (e) { if (!/not proven live/.test(e.message)) throw e; /* already gone */ }
Defensive patterns

Strategy: try-catch

Validate before calling

const proof = readExactPaneProofSync(paneId);
if (proof.status === 'gone') return; // already cleaned

Type guard

const paneIsGone = (p: ExactPaneProof): boolean => p.status === 'gone';

Try / catch

catch (e) { if (/not proven live/.test(e.message)) return; /* idempotent success */ throw e; }

Prevention

When it happens

Trigger: Calling kill/cleanup paths (requireLiveExactPaneSync) for a pane that tmux already deleted: normal shutdown race where the pane exited first, or a previous cleanup already killed it.

Common situations: Idempotent cleanup retries; agent pane finished and closed before the sweeper ran; double-shutdown paths.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/730613173e0df104. Report an issue: GitHub.