{"record":{"id":"c5954adc4dfc0c14","repo":"medusajs/medusa","slug":"cannot-retry-step-when-status-is-step-getstates","errorCode":null,"errorMessage":"Cannot retry step when status is ${step.getStates().status}","messagePattern":"Cannot retry step when status is (.+?)","errorType":"exception","errorClass":"MedusaError","httpStatus":null,"severity":"error","filePath":"packages/core/orchestration/src/transaction/transaction-orchestrator.ts","lineNumber":1946,"sourceCode":"    const [curTransaction, step] =\n      await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(\n        responseIdempotencyKey,\n        handler,\n        transaction\n      )\n\n    if (onLoad) {\n      await onLoad(curTransaction)\n    }\n\n    if (step.getStates().status === TransactionStepStatus.WAITING) {\n      this.emit(DistributedTransactionEvent.RESUME, {\n        transaction: curTransaction,\n      })\n\n      await TransactionOrchestrator.retryStep(curTransaction, step)\n    } else {\n      throw new MedusaError(\n        MedusaError.Types.NOT_ALLOWED,\n        `Cannot retry step when status is ${step.getStates().status}`\n      )\n    }\n\n    return curTransaction\n  }\n\n  /** Register a step success for a specific transaction and step\n   * @param responseIdempotencyKey - The idempotency key for the step\n   * @param handler - The handler function to execute the step\n   * @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey\n   * @param response - The response of the step\n   */\n  public async registerStepSuccess({\n    responseIdempotencyKey,\n    handler,\n    transaction,","sourceCodeStart":1928,"sourceCodeEnd":1964,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/orchestration/src/transaction/transaction-orchestrator.ts#L1928-L1964","documentation":"retryStep/retry APIs re-run a previously failed step. A retry is only meaningful when the step is in a failed/idle-retryable state; retrying a step that is currently invoking, already succeeded, or whose state otherwise doesn't allow it throws MedusaError NOT_ALLOWED with the actual status included.","triggerScenarios":"Calling retry on a step whose getStates().status is not failed (e.g. it is still running, was skipped, or already completed), or double-retrying after a retry already moved the step back to invocation.","commonSituations":"Admin 'retry' buttons acting on stale step state; racing a retry with the step finishing; retrying steps in workflows that already transitioned to compensating/failed permanently.","solutions":["Load fresh transaction state and verify the target step's status is failed (and the flow is in a retryable state) before calling retry","Disable the retry action in UI/logic unless status === 'failed'","For flows already compensating, restart the workflow instead of retrying a step"],"exampleFix":"// before\nawait orchestrator.retry(transactionId, action)\n\n// after\nconst [, step] = await orchestrator.getTransactionStep(transactionId, action)\nif (step.getStates().status !== 'failed') {\n  return { retried: false, status: step.getStates().status }\n}\nawait orchestrator.retry(transactionId, action)","handlingStrategy":"validation","validationCode":"const [, step] = await orchestrator.getTransactionStep(transactionId, action)\nif (step.getStates().status === 'failed') {\n  await orchestrator.retry(transactionId, action)\n} else {\n  return { retried: false, status: step.getStates().status }\n}","typeGuard":null,"tryCatchPattern":"try { await orchestrator.retry(transactionId, action) } catch (e) { if (e.type === 'not_allowed' && /Cannot retry/.test(e.message)) return { retried: false } throw e }","preventionTips":["Gate retry buttons on live step status === failed","Restart the workflow instead of retrying when the flow is already compensating"],"tags":["orchestration","workflow","retry","invalid-state"],"backgroundTag":"invalid-workflow-state-transition","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}