medusajs/medusa · error · MedusaError
TransactionModel "${transaction.modelId}" cannot be orchestr
Error message
TransactionModel "${transaction.modelId}" cannot be orchestrated by "${this.id}" model. What it means
TransactionOrchestrator.resume(transaction) can only resume transactions that belong to the same workflow model. Every DistributedTransaction carries the modelId of the flow definition it was created from; resuming with an orchestrator whose id differs throws NOT_ALLOWED to prevent applying the wrong flow to saved state.
Source
Thrown at packages/core/orchestration/src/transaction/transaction-orchestrator.ts:1406
if (ret.transactionIsCancelling) {
await this.cancelTransaction(transaction, {
preventExecuteNext: true,
})
}
if (isAsync && !ret.stopExecution) {
// Schedule to continue the execution of async steps because they are not awaited on purpose and can be handled by another machine
await transaction.scheduleRetry(step, 0)
}
}
/**
* Start a new transaction or resume a transaction that has been previously started
* @param transaction - The transaction to resume
*/
public async resume(transaction: DistributedTransactionType): Promise<void> {
if (transaction.modelId !== this.id) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
`TransactionModel "${transaction.modelId}" cannot be orchestrated by "${this.id}" model.`
)
}
if (transaction.hasFinished()) {
return
}
const executeNext = async () => {
const flow = transaction.getFlow()
if (flow.state === TransactionState.NOT_STARTED) {
flow.state = TransactionState.INVOKING
flow.startedAt = Date.now()
await transaction.saveCheckpoint({
ttl: flow.hasAsyncSteps ? 0 : TransactionOrchestrator.DEFAULT_TTL,View on GitHub (pinned to 5e06e544a2)
Solutions
- Resume with the orchestrator whose workflow id matches transaction.modelId
- If a workflow was renamed, use the old orchestrator (or same id) to resume in-flight transactions, then migrate
- Filter by modelId when loading transactions from storage before resuming
Example fix
// before
await someOtherOrchestrator.resume(loadedTx)
// after
if (loadedTx.modelId === orchestrator.id) {
await orchestrator.resume(loadedTx)
} Defensive patterns
Strategy: type-guard
Type guard
const canResume = (orchestrator, tx) => tx.modelId === orchestrator.id && !tx.hasFinished()
Try / catch
try { await orchestrator.resume(tx) } catch (e) { if (e.type === 'not_allowed' && /cannot be orchestrated/.test(e.message)) { /* route to correct orchestrator */ } throw e } Prevention
- Never rename workflow ids while transactions are in flight
- Always pair a loaded transaction with the orchestrator of the same model id
When it happens
Trigger: Calling orchestratorA.resume(transactionB) where transactionB.modelId !== orchestratorA.id — e.g. loading a transaction from storage with one workflow's orchestrator and resuming it with another, or resuming after renaming a workflow's id.
Common situations: Renaming a workflow (its id) while old transactions still exist in storage; sharing a storage across multiple workflow engines and mixing up orchestrator instances; code that fetches 'any' transaction and resumes it generically.
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/cc0c9e5cc5a63087.
Report an issue: GitHub.