{"record":{"id":"972e971bdfdb6b3f","repo":"eyaltoledano/claude-task-master","slug":"workflow-has-been-aborted","errorCode":null,"errorMessage":"Workflow has been aborted","messagePattern":"Workflow has been aborted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts","lineNumber":104,"sourceCode":"\t\t\treturn this.context.currentTDDPhase || 'RED';\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Get workflow context\n\t */\n\tgetContext(): WorkflowContext {\n\t\treturn { ...this.context };\n\t}\n\n\t/**\n\t * Transition to next state based on event\n\t */\n\tasync transition(event: WorkflowEvent): Promise<void> {\n\t\t// Check if workflow is aborted\n\t\tif (this.aborted && event.type !== 'ABORT') {\n\t\t\tthrow new Error('Workflow has been aborted');\n\t\t}\n\n\t\t// Handle special events that work across all phases\n\t\tif (event.type === 'ERROR') {\n\t\t\tthis.handleError(event.error);\n\t\t\tawait this.triggerAutoPersist();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.type === 'ABORT') {\n\t\t\tthis.aborted = true;\n\t\t\tawait this.triggerAutoPersist();\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.type === 'RETRY') {\n\t\t\tthis.handleRetry();\n\t\t\tawait this.triggerAutoPersist();","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/eyaltoledano/claude-task-master/blob/c0c98d367c55296bfe69e65680625b6db437af02/packages/tm-core/src/modules/workflow/orchestrators/workflow-orchestrator.ts#L86-L122","documentation":"WorkflowOrchestrator.transition() refuses to process any event except ABORT once the workflow has been aborted — the aborted flag latches and the state machine is terminal. This prevents events (e.g. PHASE_COMPLETE, COMMIT) from mutating an aborted workflow's state.","triggerScenarios":"Dispatching any non-ABORT WorkflowEvent via transition() (directly or through startWorkflow, completePhase, commit, finalizeWorkflow) after abortWorkflow() was called or the aborted flag was otherwise set.","commonSituations":"A caller keeps a stale orchestrator reference and continues driving it after an abort; an async callback queued a phase-complete event that fires after a concurrent abort; retrying work on an aborted workflow instead of starting a fresh one.","solutions":["Stop dispatching events to this orchestrator; treat it as terminal.","If the workflow should continue, create a new orchestrator instance (or resume from persisted state) instead of reusing the aborted one.","If you intended to shut down, send the explicit ABORT event, which is the only permitted event.","Audit code paths that call abortWorkflow() concurrently with in-flight transitions; guard callbacks with an isAborted check."],"exampleFix":"// before\nawait orchestrator.abortWorkflow();\nawait orchestrator.transition({ type: 'PHASE_COMPLETE' }); // throws\n// after\nawait orchestrator.abortWorkflow();\nconst fresh = new WorkflowOrchestrator(/* restored state */);\nawait fresh.startWorkflow(/* ... */);","handlingStrategy":"try-catch","validationCode":"// before dispatching\nif (orchestrator.isAborted?.() ?? abortedFlag) {\n  throw new SkipDispatchError('workflow already aborted');\n}","typeGuard":"function canDispatch(o, event) {\n  return !(o.isAborted?.() ?? o.aborted) || event.type === 'ABORT';\n}","tryCatchPattern":"try {\n  await orchestrator.transition(event);\n} catch (e) {\n  if (e.message === 'Workflow has been aborted') {\n    // treat as terminal: stop dispatching, optionally start new orchestrator\n    return;\n  }\n  throw e;\n}","preventionTips":["Track aborted state in the caller and stop event loops when set","Guard async callbacks with an isAborted check before firing transitions","Never reuse an aborted orchestrator; create or resume a new one","Use the ABORT event for intentional shutdown paths"],"tags":["state-machine","workflow","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"c0c98d367c55296bfe69e65680625b6db437af02","analyzedAt":"2026-08-29T02:56:26.071Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}