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

startup_cleanup_pane_owner_unavailable:missing_team_owner_id

Error message

startup_cleanup_pane_owner_unavailable:missing_team_owner_id

What it means

reconcileStartupCleanupPanes must kill leftover startup panes via tmux, and to do that safely it needs the team's pane-owner tag ID (tmux_pane_owner_id) to verify ownership. If cleanup panes are configured but that owner id is missing/blank, there is no way to prove the panes belong to this team, so it refuses rather than risk killing foreign panes.

Source

Thrown at src/team/runtime.ts:384

          startup_cleanup_panes: current.startup_cleanup_panes,
        }, null, 2),
      },
    });
    const committed = await readTeamConfig(current.name, cwd);
    if (!committed) throw new Error(`shutdown_config_missing_after_commit:${current.name}`);
    return committed;
  });
}

export async function reconcileStartupCleanupPanes(
  config: TeamConfig,
  cwd: string,
): Promise<TeamConfig> {
  const cleanupPanes = config.startup_cleanup_panes ?? [];
  if (cleanupPanes.length === 0) return config;

  const teamPaneOwnerId = config.tmux_pane_owner_id?.trim() ?? '';
  if (!teamPaneOwnerId) throw new Error('startup_cleanup_pane_owner_unavailable:missing_team_owner_id');

  const resolvedPaneIds = new Set<string>();
  const expectedPanePids: Record<string, number> = {};
  for (const cleanupPane of cleanupPanes) {
    if (cleanupPane.pane_id === config.leader_pane_id || cleanupPane.pane_id === config.hud_pane_id) {
      throw new Error(`startup_cleanup_pane_target_invalid:${cleanupPane.pane_id}`);
    }
    const proof = readExactPaneProofSync(cleanupPane.pane_id);
    if (proof.status === 'gone') {
      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}`);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Restore/set tmux_pane_owner_id in the team config (a non-empty unique owner tag)
  2. Clear startup_cleanup_panes if the panes are already gone and no owner proof is needed
  3. Recreate the team with the current version so the config is fully populated

Example fix

// before
{ startup_cleanup_panes: [{pane_id: '%5', pid: 123}] }
// after
{ tmux_pane_owner_id: 'omx-team-myteam-ab12', startup_cleanup_panes: [{pane_id: '%5', pid: 123}] }
Defensive patterns

Strategy: validation

Validate before calling

if ((config.startup_cleanup_panes?.length ?? 0) > 0 && !(config.tmux_pane_owner_id?.trim())) {
  config.startup_cleanup_panes = undefined; // or fail with a clear message
}

Try / catch

try { await reconcileStartupCleanupPanes(config, cwd); } catch (e) { if (e instanceof Error && e.message === 'startup_cleanup_pane_owner_unavailable:missing_team_owner_id') { /* populate owner id or clear cleanup panes */ } throw e; }

Prevention

When it happens

Trigger: A config with non-empty startup_cleanup_panes but tmux_pane_owner_id unset, empty, or whitespace — typically hand-edited or migrated configs predating the owner-tag feature.

Common situations: Configs created by an older version before tmux_pane_owner_id existed; manual config.json edits stripping the field; tests with minimal fixture configs.

Related errors


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