{"record":{"id":"022df22fbb2e7269","repo":"medusajs/medusa","slug":"action-not-found","errorCode":null,"errorMessage":"Action not found.","messagePattern":"Action not found\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/orchestration/src/transaction/transaction-orchestrator.ts","lineNumber":1858,"sourceCode":"        )\n      }\n\n      transaction = new DistributedTransaction(\n        existingTransaction.flow,\n        handler!,\n        undefined,\n        existingTransaction.errors,\n        existingTransaction.context\n      )\n    }\n\n    const step = TransactionOrchestrator.getStepByAction(\n      transaction.getFlow(),\n      action\n    )\n\n    if (step === null) {\n      throw new Error(\"Action not found.\")\n    } else if (\n      step.isCompensating()\n        ? actionType !== TransactionHandlerType.COMPENSATE\n        : actionType !== TransactionHandlerType.INVOKE\n    ) {\n      throw new Error(\"Incorrect action type.\")\n    }\n    return [transaction, step]\n  }\n\n  /** Skip the execution of 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   */\n  public async skipStep({\n    responseIdempotencyKey,\n    handler,","sourceCodeStart":1840,"sourceCodeEnd":1876,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/orchestration/src/transaction/transaction-orchestrator.ts#L1840-L1876","documentation":"After resolving a transaction from a responseIdempotencyKey, the orchestrator looks up the step named by the key's action segment in the transaction's flow. If getStepByAction returns null, the action does not exist in that flow — a malformed key or a flow-definition change removed the step.","triggerScenarios":"Processing a step response whose idempotency key contains an action alias that is not present in the loaded transaction's flow — e.g. keys generated by an older workflow version, hand-built keys, or keys decoded incorrectly (separator mismatch).","commonSituations":"Deploying a workflow change that renamed/removed a step while old async callbacks with old keys still arrive; constructing responseIdempotencyKey manually with a wrong action segment.","solutions":["Verify the action segment of the key matches a current step alias in the workflow definition","Discard/ack stale callbacks whose keys reference removed steps","Never hand-build idempotency keys; use the key returned by the engine"],"exampleFix":"// before\nconst key = `${modelId}:${txId}:oldStepName:invoke`\nawait orchestrator.transactionStepResponse(key)\n\n// after\n// use the key provided by the engine (e.g. from step.context.idempotencyKey)\nawait orchestrator.transactionStepResponse(engineProvidedKey)","handlingStrategy":"try-catch","validationCode":"// Validate key structure before use\nconst [modelId, txId, action, type] = key.split(':')\nif (!modelId || !txId || !action || !['invoke', 'compensate'].includes(type)) {\n  throw new Error(`Malformed idempotency key: ${key}`)\n}","typeGuard":null,"tryCatchPattern":"try { await orchestrator.transactionStepResponse(key, null, handler) } catch (e) { if (/Action not found/.test(e.message)) return /* stale key */ throw e }","preventionTips":["Echo back engine-issued idempotency keys verbatim; never construct them manually","Version workflow names when step sets change, and drop callbacks for old versions"],"tags":["orchestration","workflow","step-not-found","idempotency-key"],"backgroundTag":"workflow-step-not-found","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}