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

failed to kill tmux pane ${killTarget}: ${killed.stderr}

Error message

failed to kill tmux pane ${killTarget}: ${killed.stderr}

What it means

Thrown when the tmux `kill-pane` command issued to terminate a worker pane exits non-zero; tmux's stderr is included. Additionally the follow-up liveness proof may reject (not shown here), but this specific message means the kill command itself failed — typically because the pane already disappeared or the target is malformed.

Source

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

  const initialTarget = await resolveTarget();
  if (!initialTarget) return;
  if (await isWorkerAliveAsync(sessionName, workerIndex, workerPaneId, expectedPanePid, expectedTeamOwnerId, hudPaneId, resolveTarget) !== 'alive') return;
  await runTmuxAsync(['send-keys', '-t', initialTarget, 'C-c']);
  await sleep(1000);

  if (await isWorkerAliveAsync(sessionName, workerIndex, workerPaneId, expectedPanePid, expectedTeamOwnerId, hudPaneId, resolveTarget) === 'alive') {
    const exitTarget = await resolveTarget();
    if (exitTarget) {
      await runTmuxAsync(['send-keys', '-t', exitTarget, 'C-d']);
      await sleep(1000);
    }
  }

  if (await isWorkerAliveAsync(sessionName, workerIndex, workerPaneId, expectedPanePid, expectedTeamOwnerId, hudPaneId, resolveTarget) === 'alive') {
    const killTarget = await resolveTarget();
    if (!killTarget) return;
    const killed = await runTmuxAsync(['kill-pane', '-t', killTarget]);
    if (!killed.ok) throw new Error(`failed to kill tmux pane ${killTarget}: ${killed.stderr}`);
    const absence = await readExactPaneProof(killTarget);
    if (absence.status === 'unavailable') throw new ExactPaneProofUnavailableError(absence);
    if (absence.status !== 'gone') throw new Error(`tmux pane remains live after kill: ${killTarget}`);
  }
}


// Explicit pane targets require frozen PID, canonical Team owner, and HUD
// exclusion. A successful kill is only accepted after a fresh absence proof.
export function killWorkerByPaneId(
  workerPaneId: string,
  expectedPanePid?: number,
  leaderPaneId?: string,
  expectedTeamOwnerId?: string,
  hudPaneId?: string,
): void {
  const expectedOwner = typeof expectedTeamOwnerId === 'string' ? expectedTeamOwnerId.trim() : '';
  if (!hasExplicitWorkerPaneId(workerPaneId)

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Treat 'no such pane' / 'can't find session' stderr as success — the pane is already gone; skip or ignore
  2. Re-check liveness (isWorkerAliveAsync) immediately before kill to shrink the race window
  3. Serialize cleanup handlers so kill runs exactly once
  4. Re-resolve the target right before killing instead of reusing a cached one

Example fix

// before
const killed = await runTmuxAsync(['kill-pane', '-t', target]);
if (!killed.ok) throw new Error(killed.stderr);

// after
const killed = await runTmuxAsync(['kill-pane', '-t', target]);
if (!killed.ok && !/no such pane|can't find session/.test(killed.stderr)) {
  throw new Error(killed.stderr);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ((await isWorkerAliveAsync(sessionName, idx, paneId)) !== 'alive') return; // nothing to kill

Try / catch

try { await killWorkerPane(target); } catch (err) { if (!/no such pane|can't find session/.test(String(err))) throw err; }

Prevention

When it happens

Trigger: Calling the worker-kill routine while the pane is concurrently closing (agent exits on its own), the session is already dead, or resolveTarget returns a stale/malformed target string at kill time.

Common situations: Race between worker process exit and orchestrator cleanup; kill issued during tmux server shutdown; double-kill from overlapping cleanup handlers; stale pane id from a previous session.

Related errors


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