eyaltoledano/claude-task-master · error
Invalid transition: ${event.type} in SUBTASK_LOOP
Error message
Invalid transition: ${event.type} in SUBTASK_LOOP What it means
While the workflow is in the SUBTASK_LOOP phase, handleTDDPhaseTransition only accepts a fixed set of events: RED_PHASE_COMPLETE, GREEN_PHASE_COMPLETE, COMMIT_COMPLETE, SUBTASK_COMPLETE, and ALL_SUBTASKS_COMPLETE. This default branch throws for any other event.type, protecting the state machine from unknown or phase-mismatched events (e.g. workflow-level events meant for other phases such as start/finalize events).
Source
Thrown at packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts:289
this.emit('tdd:red:started');
this.emit('subtask:started');
} else {
// All subtasks complete, transition to FINALIZE
await this.transition({ type: 'ALL_SUBTASKS_COMPLETE' });
}
break;
case 'ALL_SUBTASKS_COMPLETE':
// Transition to FINALIZE phase
this.emit('phase:exited');
this.currentPhase = 'FINALIZE';
this.context.currentTDDPhase = undefined;
this.emit('phase:entered');
// Note: Don't auto-transition to COMPLETE - requires explicit finalize call
break;
default:
throw new Error(`Invalid transition: ${event.type} in SUBTASK_LOOP`);
}
}
/**
* Execute a state transition
*/
private executeTransition(
transition: StateTransition,
event: WorkflowEvent
): void {
// Check guard condition if present
if (transition.guard && !transition.guard(this.context)) {
throw new Error(
`Guard condition failed for transition to ${transition.to}`
);
}
// Check phase-specific guard if presentView on GitHub (pinned to c0c98d367c)
Solutions
- Use one of the valid SUBTASK_LOOP events: RED_PHASE_COMPLETE, GREEN_PHASE_COMPLETE, COMMIT_COMPLETE, SUBTASK_COMPLETE, or ALL_SUBTASKS_COMPLETE.
- Check the event type string for typos and exact casing against the WorkflowEvent type definition.
- If you need to finalize or move to another workflow phase, use the orchestrator's dedicated API (e.g. the explicit finalize call) instead of transitioning events while in SUBTASK_LOOP.
- Update callers if the event was renamed in a newer version of @tm/core.
Example fix
// before
await orchestrator.transition({ type: 'SUBTASK_COMPLETED' }); // typo
// after
await orchestrator.transition({ type: 'SUBTASK_COMPLETE' }); Defensive patterns
Strategy: validation
Validate before calling
const SUBTASK_LOOP_EVENTS = ['RED_PHASE_COMPLETE', 'GREEN_PHASE_COMPLETE', 'COMMIT_COMPLETE', 'SUBTASK_COMPLETE', 'ALL_SUBTASKS_COMPLETE'] as const;
if (!SUBTASK_LOOP_EVENTS.includes(event.type as never)) {
throw new Error(`Event ${event.type} not allowed while in SUBTASK_LOOP`);
} Type guard
type SubtaskLoopEvent = 'RED_PHASE_COMPLETE' | 'GREEN_PHASE_COMPLETE' | 'COMMIT_COMPLETE' | 'SUBTASK_COMPLETE' | 'ALL_SUBTASKS_COMPLETE';
function isSubtaskLoopEvent(t: string): t is SubtaskLoopEvent {
return (['RED_PHASE_COMPLETE', 'GREEN_PHASE_COMPLETE', 'COMMIT_COMPLETE', 'SUBTASK_COMPLETE', 'ALL_SUBTASKS_COMPLETE'] as const).includes(t as SubtaskLoopEvent);
} Try / catch
try {
await orchestrator.transition(event);
} catch (e) {
if (e instanceof Error && e.message.includes('in SUBTASK_LOOP')) {
// event.type is not valid here — map it to a valid SUBTASK_LOOP event or use the phase-specific API
} else {
throw e;
}
} Prevention
- Use TypeScript discriminated unions for WorkflowEvent so invalid event types fail at compile time.
- Keep a central constant/list of valid events per workflow phase and validate against it before dispatching.
- Check event type spelling/casing when upgrading @tm-core versions in case event names changed.
When it happens
Trigger: Calling transition({ type: '<anything else>' }) while currentPhase is SUBTASK_LOOP — e.g. START, FINALIZE_COMPLETE, PAUSE, a typo like 'SUBTASK_COMPLETED', or an event belonging to the PLANNING/FINALIZE phases.
Common situations: Typo in the event type string; sending workflow-level control events during the subtask loop; copy-pasted dispatch code from a different phase; a version change renaming or removing an event type so older callers send obsolete names.
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
- Workflow has been aborted
- Invalid transition: ${event.type} from ${this.currentPhase}
- Invalid transition: RED_PHASE_COMPLETE from non-RED phase
- Invalid transition: GREEN_PHASE_COMPLETE from non-GREEN phas
- Invalid transition: COMMIT_COMPLETE from non-COMMIT phase
AI-assisted analysis of eyaltoledano/claude-task-master@c0c98d367c (2026-08-29).
Data as JSON: /api/errors/3cbdda56ffbc3b26.
Report an issue: GitHub.