eyaltoledano/claude-task-master · error
Invalid transition: COMMIT_COMPLETE from non-COMMIT phase
Error message
Invalid transition: COMMIT_COMPLETE from non-COMMIT phase
What it means
COMMIT is the final phase of each subtask's TDD cycle (after GREEN passes). This error is thrown by handleTDDPhaseTransition when a COMMIT_COMPLETE event is dispatched while the current TDD phase is not 'COMMIT' — the GREEN phase has not been completed (or the cycle already moved on). It preserves the invariant that a subtask can only be marked completed after tests passed and the GREEN -> COMMIT step occurred.
Source
Thrown at packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts:242
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');
this.context.currentTDDPhase = 'COMMIT';
this.emit('tdd:commit:started');
break;
case 'COMMIT_COMPLETE':
if (currentTDD !== 'COMMIT') {
throw new Error(
'Invalid transition: COMMIT_COMPLETE from non-COMMIT phase'
);
}
this.emit('tdd:commit:completed');
// Mark current subtask as completed
const currentSubtask =
this.context.subtasks[this.context.currentSubtaskIndex];
if (currentSubtask) {
currentSubtask.status = 'completed';
}
break;
case 'SUBTASK_COMPLETE':
this.emit('subtask:completed');
// Move to next subtask
this.context.currentSubtaskIndex++;
// Emit progress updateView on GitHub (pinned to c0c98d367c)
Solutions
- Verify the phase is 'COMMIT' before dispatching; if still in GREEN, first complete GREEN with passing testResults via GREEN_PHASE_COMPLETE.
- If already committed, do not send COMMIT_COMPLETE again — send SUBTASK_COMPLETE (or wait for the orchestrator's own flow) instead.
- If the orchestrator advanced to a new subtask, restart its cycle at RED_PHASE_COMPLETE rather than sending COMMIT_COMPLETE.
- Check whether events are being replayed/duplicated by your dispatch layer and add idempotency guards.
Example fix
// before
await orchestrator.transition({ type: 'COMMIT_COMPLETE' });
// after
const ctx = orchestrator.getContext();
if (ctx.currentTDDPhase === 'COMMIT') {
await orchestrator.transition({ type: 'COMMIT_COMPLETE' });
} else if (ctx.currentTDDPhase === 'GREEN') {
await orchestrator.transition({ type: 'GREEN_PHASE_COMPLETE', testResults });
await orchestrator.transition({ type: 'COMMIT_COMPLETE' });
} Defensive patterns
Strategy: validation
Validate before calling
const ctx = orchestrator.getContext();
if (ctx.currentTDDPhase !== 'COMMIT') {
throw new Error(`Cannot complete COMMIT: current TDD phase is ${ctx.currentTDDPhase ?? 'RED'}`);
} Type guard
function canCompleteCommit(ctx: { currentTDDPhase?: 'RED' | 'GREEN' | 'COMMIT' }): ctx is { currentTDDPhase: 'COMMIT' } {
return ctx.currentTDDPhase === 'COMMIT';
} Try / catch
try {
await orchestrator.transition({ type: 'COMMIT_COMPLETE' });
} catch (e) {
if (e instanceof Error && e.message.includes('COMMIT_COMPLETE from non-COMMIT')) {
// inspect orchestrator.getContext().currentTDDPhase and resume the cycle from the correct event
} else {
throw e;
}
} Prevention
- Only send COMMIT_COMPLETE after observing the 'tdd:commit:started' event.
- Do not resend COMMIT_COMPLETE after success — make dispatch idempotent.
- Model the per-subtask cycle as an explicit local state machine mirroring RED -> GREEN -> COMMIT.
When it happens
Trigger: Calling transition({ type: 'COMMIT_COMPLETE' }) while currentTDDPhase is 'RED' or 'GREEN'; sending COMMIT_COMPLETE twice (second time the phase has moved on); dispatching COMMIT_COMPLETE right after SUBTASK_COMPLETE started the next subtask (phase reset to RED).
Common situations: Automations that try to shortcut the cycle by jumping straight to commit; retry logic re-sending COMMIT_COMPLETE after a transient failure; event replay from a queue delivering COMMIT_COMPLETE before GREEN_PHASE_COMPLETE.
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: GREEN_PHASE_COMPLETE from non-GREEN phas
- 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/42bc095995f63a9b.
Report an issue: GitHub.