medusajs/medusa · error · Error
Step ${obj.action} is already defined in workflow.
Error message
Step ${obj.action} is already defined in workflow. What it means
While flattening the workflow graph into executable step ids, the orchestrator records every action alias it visits. Encountering the same alias twice means two steps share one action name, which would break step addressing/idempotency keys — so it throws immediately at workflow definition time.
Source
Thrown at packages/core/orchestration/src/transaction/transaction-orchestrator.ts:1620
const actionNames = new Set<string>()
const queue: any[] = [
{ obj: flow, level: [TransactionOrchestrator.ROOT_STEP] },
]
const features = {
hasAsyncSteps: false,
hasStepTimeouts: false,
hasRetriesTimeout: false,
hasNestedTransactions: false,
}
while (queue.length > 0) {
const { obj, level } = queue.shift()
if (obj.action) {
if (actionNames.has(obj.action)) {
throw new Error(`Step ${obj.action} is already defined in workflow.`)
}
actionNames.add(obj.action)
level.push(obj.action)
const id = level.join(".")
const parent = level.slice(0, level.length - 1).join(".")
if (!existingSteps || parent === TransactionOrchestrator.ROOT_STEP) {
states[parent].next?.push(id)
}
const definitionCopy = { ...obj } as TransactionStepsDefinition
delete definitionCopy.next
const isAsync = !!definitionCopy.async
const hasRetryInterval = !!(
definitionCopy.retryInterval || definitionCopy.retryIntervalAwaiting
)View on GitHub (pinned to 5e06e544a2)
Solutions
- Give every step a unique alias/name within the workflow (pass distinct names to createStep or rename on compose)
- Do not reuse the same step node object in multiple positions of the graph; create separate instances
- When composing parallel branches, prefix sub-workflow step names (namespacing)
Example fix
// before
const sameStep = createStep('charge', ...)
createWorkflow('w', () => { sameStep(); sameStep() })
// after
const chargeA = createStep('charge-a', ...)
const chargeB = createStep('charge-b', ...)
createWorkflow('w', () => { chargeA(); chargeB() }) Defensive patterns
Strategy: validation
Validate before calling
const names = new Set()
for (const s of mySteps) {
if (names.has(s.__action__)) throw new Error(`duplicate step ${s.__action__}`)
names.add(s.__action__)
} Prevention
- Never reuse a single step node in two graph positions; instantiate per position
- Namespace step aliases when embedding sub-workflows in parallel branches
When it happens
Trigger: Defining a workflow where the same step alias is inserted twice (e.g. appending a step object that is reused in two places, or two createStep calls that got the same generated/assigned alias via .invoke config reuse), or composing sub-workflows that both expose a step with the same name.
Common situations: Reusing a step instance variable in two parts of a flow; copy-pasting a step definition and forgetting to change its alias; merging parallel branches that contain identically named steps.
Related errors
- Method 'clearTransactionTimeout' not implemented.
- Method 'scheduleStepTimeout' not implemented.
- Method 'clearStepTimeout' not implemented.
- Method 'clearExpiredExecutions' not implemented.
- Unable to serialize context object. Please make sure the wor
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/ac9248637bc43141.
Report an issue: GitHub.