eyaltoledano/claude-task-master · error
Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phas
Error message
Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase
What it means
The TDD workflow orchestrator enforces the strict RED -> GREEN -> COMMIT cycle per subtask. This error is thrown in handleTDDPhaseTransition when a GREEN_PHASE_COMPLETE event arrives while context.currentTDDPhase is not 'GREEN' (e.g. still RED or COMMIT). The orchestrator tracks the current TDD phase internally, and completing the GREEN phase out of order would break the state machine invariant that tests were written (RED) before implementation (GREEN).
Source
Thrown at packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts:217
this.context.currentTDDPhase = 'RED';
this.emit('tdd:red:started');
this.emit('subtask:started');
} else {
// All subtasks complete, transition to FINALIZE
await this.transition({ type: 'ALL_SUBTASKS_COMPLETE' });
}
break;
}
// Normal RED phase: has failing tests, proceed to GREEN
this.emit('tdd:red:completed');
this.context.currentTDDPhase = 'GREEN';
this.emit('tdd:green:started');
break;
case 'GREEN_PHASE_COMPLETE':
if (currentTDD !== 'GREEN') {
throw new Error(
'Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phase'
);
}
// Validate test results are provided
if (!event.testResults) {
throw new Error('Test results required for GREEN phase transition');
}
// Validate GREEN phase has no failures
if (event.testResults.failed !== 0) {
throw new Error('GREEN phase must have zero failures');
}
// Store test results in context
this.context.lastTestResults = event.testResults;
this.emit('tdd:green:completed');View on GitHub (pinned to c0c98d367c)
Solutions
- Check the current phase before transitioning: only send GREEN_PHASE_COMPLETE when context.currentTDDPhase === 'GREEN'.
- If still in RED, first emit RED_PHASE_COMPLETE (with test results showing at least one failing test) so the orchestrator moves to GREEN.
- If in COMMIT, the GREEN phase was already completed — do not re-send GREEN_PHASE_COMPLETE; send COMMIT_COMPLETE instead.
- For a new subtask, phases restart at RED; send RED_PHASE_COMPLETE first, not GREEN_PHASE_COMPLETE.
- Inspect emitted events ('tdd:green:started') or logged state to confirm the phase actually advanced before sending the completion event.
Example fix
// before
await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });
// after
if (orchestrator.getContext().currentTDDPhase === 'GREEN') {
await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });
} else {
// advance through RED first
await orchestrator.transition({ type: 'RED_PHASE_COMPLETE', testResults });
} Defensive patterns
Strategy: validation
Validate before calling
const ctx = orchestrator.getContext();
if (ctx.currentTDDPhase !== 'GREEN') {
throw new Error(`Cannot complete GREEN phase: current TDD phase is ${ctx.currentTDDPhase ?? 'RED'}`);
} Type guard
function canCompleteGreen(ctx: { currentTDDPhase?: 'RED' | 'GREEN' | 'COMMIT' }): ctx is { currentTDDPhase: 'GREEN' } {
return ctx.currentTDDPhase === 'GREEN';
} Try / catch
try {
await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });
} catch (e) {
if (e instanceof Error && e.message.includes('GREEN_PHASE_COMPLETE from non-GREEN')) {
// re-sync: inspect orchestrator.getContext().currentTDDPhase and resume from the correct event
} else {
throw e;
}
} Prevention
- Always drive the cycle through the full RED -> GREEN -> COMMIT sequence per subtask.
- Read context.currentTDDPhase (or listen for tdd:*:started events) before dispatching any phase-completion event.
- Make dispatch code idempotent so retries cannot resend an already-processed phase event.
When it happens
Trigger: Calling orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults }) when currentTDDPhase is 'RED' (skipping RED_PHASE_COMPLETE), or when it is 'COMMIT' (double-completing GREEN), or after phase was reset to undefined/RED (e.g. after ALL_SUBTASKS_COMPLETE or SUBTASK_COMPLETE moved to a new subtask).
Common situations: Driving the orchestrator from custom automation scripts instead of the normal TDD flow; replaying or re-dispatching events after a retry causes duplicate GREEN_PHASE_COMPLETE; resuming a persisted workflow whose phase was restored incorrectly; assuming phases can be skipped because tests already pass.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Invalid transition: COMMIT_COMPLETE from non-COMMIT phase
- Invalid transition: ${event.type} from ${this.currentPhase}
- Invalid transition: RED_PHASE_COMPLETE from non-RED phase
- Not in active TDD phase
- Cannot commit in ${tddPhase} phase. Complete RED and GREEN p
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/157422ffa5b8a69e.
Report an issue: GitHub.