{"record":{"id":"5248be15967b2108","repo":"Yeachan-Heo/oh-my-codex","slug":"startup-cleanup-pane-target-invalid-cleanuppane","errorCode":null,"errorMessage":"startup_cleanup_pane_target_invalid:${cleanupPane.pane_id}","messagePattern":"startup_cleanup_pane_target_invalid:(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/runtime.ts","lineNumber":390,"sourceCode":"    return committed;\n  });\n}\n\nexport async function reconcileStartupCleanupPanes(\n  config: TeamConfig,\n  cwd: string,\n): Promise<TeamConfig> {\n  const cleanupPanes = config.startup_cleanup_panes ?? [];\n  if (cleanupPanes.length === 0) return config;\n\n  const teamPaneOwnerId = config.tmux_pane_owner_id?.trim() ?? '';\n  if (!teamPaneOwnerId) throw new Error('startup_cleanup_pane_owner_unavailable:missing_team_owner_id');\n\n  const resolvedPaneIds = new Set<string>();\n  const expectedPanePids: Record<string, number> = {};\n  for (const cleanupPane of cleanupPanes) {\n    if (cleanupPane.pane_id === config.leader_pane_id || cleanupPane.pane_id === config.hud_pane_id) {\n      throw new Error(`startup_cleanup_pane_target_invalid:${cleanupPane.pane_id}`);\n    }\n    const proof = readExactPaneProofSync(cleanupPane.pane_id);\n    if (proof.status === 'gone') {\n      resolvedPaneIds.add(cleanupPane.pane_id);\n      continue;\n    }\n    if (proof.status === 'unavailable') {\n      assertPaneTeardownProofsAvailable('startup_cleanup', [proof]);\n      continue;\n    }\n    if (cleanupPane.pid === null) {\n      throw new Error(`startup_cleanup_pane_pid_missing:${cleanupPane.pane_id}`);\n    }\n    if (proof.pid !== cleanupPane.pid) {\n      throw new Error(`startup_cleanup_pane_identity_changed:${cleanupPane.pane_id}`);\n    }\n    const owner = readPaneTeamOwnerTagResult(cleanupPane.pane_id);\n    if (owner.status === 'error') {","sourceCodeStart":372,"sourceCodeEnd":408,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/runtime.ts#L372-L408","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copying pane ids between configs after tmux restarted and reused ids; programmatically building cleanup lists without excluding control panes.","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"],"exampleFix":"// before\ncleanup: allPaneIds\n// after\nconst cleanup = allPaneIds.filter(id => id !== config.leader_pane_id && id !== config.hud_pane_id);","handlingStrategy":"validation","validationCode":"const controlPanes = new Set([config.leader_pane_id, config.hud_pane_id].filter(Boolean));\nconst safe = cleanupPanes.filter(p => !controlPanes.has(p.pane_id));","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Exclude leader and HUD panes whenever building cleanup lists","Never copy pane ids between configs"],"tags":["team-runtime","tmux","pane-safety","config-validation"],"backgroundTag":"invalid-target-selection","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}