Yeachan-Heo/oh-my-codex · error · Error
startup_cleanup_pane_target_invalid:${cleanupPane.pane_id}
Error message
startup_cleanup_pane_target_invalid:${cleanupPane.pane_id} What it means
Each startup cleanup pane is checked against the team's leader and HUD pane ids; targeting either would kill the team's own control panes, so the reconcile aborts with the offending pane id. This is a safety invariant, not an environmental issue.
Source
Thrown at src/team/runtime.ts:390
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}`);
}
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') {View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Remove the leader/HUD pane id from startup_cleanup_panes
- Regenerate the config by restarting the team so pane roles are recorded fresh
- When building cleanup lists, always subtract leader_pane_id and hud_pane_id
Example fix
// before cleanup: allPaneIds // after const cleanup = allPaneIds.filter(id => id !== config.leader_pane_id && id !== config.hud_pane_id);
Defensive patterns
Strategy: validation
Validate before calling
const controlPanes = new Set([config.leader_pane_id, config.hud_pane_id].filter(Boolean)); const safe = cleanupPanes.filter(p => !controlPanes.has(p.pane_id));
Try / catch
try { await reconcileStartupCleanupPanes(config, cwd); } catch (e) { if (e instanceof Error && e.message.startsWith('startup_cleanup_pane_target_invalid:')) { /* remove the named pane from config and retry */ } throw e; } Prevention
- Exclude leader and HUD panes whenever building cleanup lists
- Never copy pane ids between configs
When it happens
Trigger: A startup_cleanup_panes entry whose pane_id equals config.leader_pane_id or config.hud_pane_id — from a mis-built config, stale pane id reuse, or a fixture mistake.
Common situations: Copying pane ids between configs after tmux restarted and reused ids; programmatically building cleanup lists without excluding control panes.
Related errors
- startup_cleanup_pane_owner_unavailable:missing_team_owner_id
- tmux target validation failed: ${resolved.reason}
- startup_cleanup_pane_pid_missing:${cleanupPane.pane_id}
- startup_cleanup_pane_identity_changed:${cleanupPane.pane_id}
- startup_cleanup_pane_owner_unavailable:${cleanupPane.pane_id
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/5248be15967b2108.
Report an issue: GitHub.