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

Unknown team phase: ${_exhaustive}

Error message

Unknown team phase: ${_exhaustive}

What it means

Thrown by getPhaseAgents when the phase argument does not match any known team phase ('team-plan', 'team-exec', 'team-verify', 'team-fix', etc.). The switch is exhaustive over a union type, so hitting the default branch means the runtime value escaped the compile-time union — typically deserialized config from an older/newer version or an any-cast. The `never` assignment is an exhaustiveness check, so this error signals type-system bypass, not ordinary bad input.

Source

Thrown at src/team/orchestrator.ts:138

/**
 * Get agent roles recommended for each phase
 */
export function getPhaseAgents(phase: TeamPhase): string[] {
  switch (phase) {
    case 'team-plan':
      return ['analyst', 'planner'];
    case 'team-prd':
      return ['product-manager', 'analyst'];
    case 'team-exec':
      return ['executor', 'designer', 'test-engineer'];
    case 'team-verify':
      return ['verifier', 'code-reviewer', 'quality-reviewer'];
    case 'team-fix':
      return ['executor', 'debugger', 'test-engineer'];
    default: {
      const _exhaustive: never = phase;
      throw new Error(`Unknown team phase: ${_exhaustive}`);
    }
  }
}

/**
 * Generate phase instructions for AGENTS.md context
 */
export function getPhaseInstructions(phase: TeamPhase): string {
  switch (phase) {
    case 'team-plan':
      return 'PHASE: Planning. Use /analyst for requirements, /planner for task breakdown. Output: task list with dependencies.';
    case 'team-prd':
      return 'PHASE: Requirements. Use /product-manager for PRD, /analyst for acceptance criteria. Output: explicit scope and success metrics.';
    case 'team-exec':
      return 'PHASE: Execution. Use /executor for implementation, /test-engineer for tests. Output: working code with tests.';
    case 'team-verify':
      return 'PHASE: Verification. Use /verifier for evidence collection, /quality-reviewer for review. Output: pass/fail with evidence.';
    case 'team-fix':

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Check the persisted phase state file (current_phase) for a value not in the TeamPhase union and reset/delete it
  2. Validate phase with a type guard before calling getPhaseAgents
  3. Re-run with the version of the code that wrote the phase state, or shutdown/resume the team to regenerate state

Example fix

// before
const agents = getPhaseAgents(config.current_phase as TeamPhase);
// after
const PHASES = ['team-plan','team-exec','team-verify','team-fix'] as const;
function isTeamPhase(p: string): p is TeamPhase { return (PHASES as readonly string[]).includes(p); }
const phase = config.current_phase;
const agents = isTeamPhase(phase) ? getPhaseAgents(phase) : DEFAULT_AGENTS;
Defensive patterns

Strategy: type-guard

Validate before calling

const PHASES = ['team-plan','team-exec','team-verify','team-fix'] as const;
const isTeamPhase = (p: unknown): p is TeamPhase =>
  typeof p === 'string' && (PHASES as readonly string[]).includes(p);

Type guard

function isTeamPhase(p: unknown): p is TeamPhase { return typeof p === 'string' && (PHASES as readonly string[]).includes(p); }

Try / catch

try { getPhaseAgents(phase); } catch (e) { if (e instanceof Error && e.message.startsWith('Unknown team phase')) { /* reset/reload phase state */ } throw e; }

Prevention

When it happens

Trigger: Calling getPhaseAgents(phase) with a phase string loaded from persisted team phase state (readTeamPhaseState) written by a different version, or passing a value typed as string/any that was not validated against TeamPhase before the call.

Common situations: Upgrading the team orchestrator after a phase was renamed/added, hand-editing phase state files, or reading stale state from disk after a downgrade.

Related errors


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