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

startup_worker_pane_pid_unavailable:${paneId}

Error message

startup_worker_pane_pid_unavailable:${paneId}

What it means

In interactive mode, each worker pane's PID must be recorded in the persisted team config before startup proceeds. This error means the config's workers[i-1].pid is missing, not a safe positive integer, so the runtime cannot bind identity to the pane.

Source

Thrown at src/team/runtime.ts:3842

      const identity: WorkerInfo = {
        name: bootstrapPlan.workerName,
        index: workerIndex,
        role: bootstrapPlan.workerRole,
        worker_cli: bootstrapPlan.workerCli,
        assigned_tasks: bootstrapPlan.workerTasks.map((task) => task.id),
        working_dir: workerWorkspace.cwd,
        worktree_repo_root: workerWorkspace.worktreeRepoRoot,
        worktree_path: workerWorkspace.worktreePath,
        worktree_branch: workerWorkspace.worktreeBranch,
        worktree_detached: workerWorkspace.worktreeDetached,
        worktree_created: workerWorkspace.worktreeCreated,
        team_state_root: teamStateRoot,
      };

      const persistedPanePid = config?.workers[workerIndex - 1]?.pid;
      if (workerLaunchMode === 'interactive' && paneId) {
        if (typeof persistedPanePid !== 'number' || !Number.isSafeInteger(persistedPanePid) || persistedPanePid <= 0) {
          throw new Error(`startup_worker_pane_pid_unavailable:${paneId}`);
        }
        const paneProof = readExactPaneProofSync(paneId);
        if (paneProof.status !== 'live' || paneProof.pid !== persistedPanePid) {
          throw new Error(`startup_worker_pane_identity_changed:${paneId}`);
        }
        identity.pid = persistedPanePid;
      } else if (typeof persistedPanePid === 'number') {
        identity.pid = persistedPanePid;
      }

      if (paneId) identity.pane_id = paneId;

      if (config?.workers[workerIndex - 1]) {
        config.workers[workerIndex - 1].pid = identity.pid;
        config.workers[workerIndex - 1].pane_id = paneId;
        config.workers[workerIndex - 1].role = bootstrapPlan.workerRole;
        config.workers[workerIndex - 1].worker_cli = bootstrapPlan.workerCli;
        config.workers[workerIndex - 1].assigned_tasks = bootstrapPlan.workerTasks.map((task) => task.id);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Clean up the stale team (detectAndCleanStaleTeam or manual cleanup) and start fresh so pids are persisted correctly
  2. Ensure only one leader starts the team; avoid concurrent startups sharing state
  3. If persisting configs yourself, always write worker pid as a positive safe integer
Defensive patterns

Strategy: validation

Validate before calling

const workers = config?.workers ?? [];
const pidsValid = workers.length >= workerCount && workers.slice(0, workerCount).every(w =>
  typeof w?.pid === 'number' && Number.isSafeInteger(w.pid) && w.pid > 0);
if (!pidsValid) await cleanupStaleTeam();

Type guard

const hasPersistedWorkerPid = (w: unknown): w is { pid: number } =>
  typeof (w as { pid?: unknown })?.pid === 'number' &&
  Number.isSafeInteger((w as { pid: number }).pid) && (w as { pid: number }).pid > 0;

Try / catch

try { await resumeTeam(); } catch (e) { if (/^startup_worker_pane_pid_unavailable/.test(String((e as Error).message))) { await teardownAndRestartFresh(); return; } throw e; }

Prevention

When it happens

Trigger: Interactive worker startup where paneId exists but config.workers[workerIndex-1].pid is undefined, 0, negative, a float, or an unsafe integer — e.g., config saved before pane PIDs were captured, or restored from an older state file without pid fields.

Common situations: Resuming a team from a stale or older-format config file; a crash between pane creation and config persistence; concurrent leaders racing to write the config.

Related errors


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