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

startup_cleanup_pane_owner_changed:${cleanupPane.pane_id}

Error message

startup_cleanup_pane_owner_changed:${cleanupPane.pane_id}

What it means

The pane's owner tag was read successfully but its value does not match the team's tmux_pane_owner_id (or is absent). The pane was expected to be owned by this team but is tagged otherwise (or untagged), so killing it could destroy another team's pane and the reconcile aborts.

Source

Thrown at src/team/runtime.ts:412

      resolvedPaneIds.add(cleanupPane.pane_id);
      continue;
    }
    if (proof.status === 'unavailable') {
      assertPaneTeardownProofsAvailable('startup_cleanup', [proof]);
      continue;
    }
    if (cleanupPane.pid === null) {
      throw new Error(`startup_cleanup_pane_pid_missing:${cleanupPane.pane_id}`);
    }
    if (proof.pid !== cleanupPane.pid) {
      throw new Error(`startup_cleanup_pane_identity_changed:${cleanupPane.pane_id}`);
    }
    const owner = readPaneTeamOwnerTagResult(cleanupPane.pane_id);
    if (owner.status === 'error') {
      throw new Error(`startup_cleanup_pane_owner_unavailable:${cleanupPane.pane_id}:${owner.error}`);
    }
    if (owner.status !== 'value' || owner.value !== teamPaneOwnerId) {
      throw new Error(`startup_cleanup_pane_owner_changed:${cleanupPane.pane_id}`);
    }
    expectedPanePids[cleanupPane.pane_id] = cleanupPane.pid;
  }

  const livePaneIds = Object.keys(expectedPanePids);
  if (livePaneIds.length > 0) {
    const teardown = await teardownWorkerPanes(livePaneIds, {
      leaderPaneId: config.leader_pane_id,
      hudPaneId: config.hud_pane_id,
      expectedPanePids,
      authorizePaneKill: (paneId, proof) => {
        if (expectedPanePids[paneId] !== proof.pid) return false;
        const owner = readPaneTeamOwnerTagResult(paneId);
        return owner.status === 'value' && owner.value === teamPaneOwnerId;
      },
    });
    assertPaneTeardownProofsAvailable('startup_cleanup', teardown.proofUnavailable);
    for (const paneId of teardown.killedPaneIds) resolvedPaneIds.add(paneId);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the pane's owner tag (`tmux show-options -p -t <pane> @team-owner` style) and determine which team owns it
  2. Kill the pane manually if you confirm it is leftover, then remove it from startup_cleanup_panes
  3. Avoid running multiple teams against the same tmux session unless the runtime supports it
Defensive patterns

Strategy: try-catch

Validate before calling

const owner = readPaneTeamOwnerTagResult(paneId);
if (owner.status === 'value' && owner.value !== teamPaneOwnerId) { /* foreign pane: exclude from cleanup */ }

Try / catch

try { await reconcileStartupCleanupPanes(config, cwd); } catch (e) { if (e instanceof Error && e.message.startsWith('startup_cleanup_pane_owner_changed:')) { /* inspect @team-owner option; exclude or kill manually */ } throw e; }

Prevention

When it happens

Trigger: Two teams sharing a tmux session with overlapping pane expectations; a pane re-tagged by a different team; panes whose user option was never set because they predate tagging.

Common situations: Multiple concurrent teams in one tmux session; resume-after-crash where pane tags were lost; manual tmux option edits.

Related errors


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