{"record":{"id":"0cba12e89a4a3bee","repo":"medusajs/medusa","slug":"if-a-transaction-is-not-provided-the-handler-is-r","errorCode":null,"errorMessage":"If a transaction is not provided, the handler is required","messagePattern":"If a transaction is not provided, the handler is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/orchestration/src/transaction/transaction-orchestrator.ts","lineNumber":1824,"sourceCode":"  ): TransactionStep | null {\n    for (const key in flow.steps) {\n      if (action === flow.steps[key]?.definition?.action) {\n        return flow.steps[key]\n      }\n    }\n    return null\n  }\n\n  private static async getTransactionAndStepFromIdempotencyKey(\n    responseIdempotencyKey: string,\n    handler?: TransactionStepHandler,\n    transaction?: DistributedTransactionType\n  ): Promise<[DistributedTransactionType, TransactionStep]> {\n    const [modelId, transactionId, action, actionType] =\n      responseIdempotencyKey.split(TransactionOrchestrator.SEPARATOR)\n\n    if (!transaction && !handler) {\n      throw new Error(\n        \"If a transaction is not provided, the handler is required\"\n      )\n    }\n\n    if (!transaction) {\n      const existingTransaction =\n        await TransactionOrchestrator.loadTransactionById(\n          modelId,\n          transactionId\n        )\n\n      if (existingTransaction === null) {\n        throw new MedusaError(\n          MedusaError.Types.NOT_FOUND,\n          `Transaction ${transactionId} could not be found.`\n        )\n      }\n","sourceCodeStart":1806,"sourceCodeEnd":1842,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/orchestration/src/transaction/transaction-orchestrator.ts#L1806-L1842","documentation":"TransactionOrchestrator's step-response APIs (e.g. transactionStepResponse / resumeByResponseIdempotencyKey) can resolve a transaction either from a passed DistributedTransaction or by loading it via a handler using the responseIdempotencyKey. If neither is provided, it has no way to locate the transaction and throws this plain Error.","triggerScenarios":"Calling a method like resumeByResponseIdempotencyKey/getTransactionStepByAction with a responseIdempotencyKey but passing neither a transaction object nor a handler (the function used to load the transaction from storage).","commonSituations":"Custom integrations responding to async step callbacks (e.g. webhook completing a waitFor step) where the developer only passes the idempotency key, forgetting the handler that loads persisted state.","solutions":["Pass the handler argument (loader) so the orchestrator can fetch the transaction by model/transaction id","Or pass the DistributedTransaction instance you already hold"],"exampleFix":"// before\nawait orchestrator.resumeByResponseIdempotencyKey(key)\n\n// after\nawait orchestrator.resumeByResponseIdempotencyKey(\n  key,\n  async (modelId, txId) => loadTxFromStorage(modelId, txId)\n)","handlingStrategy":"validation","validationCode":"if (!transaction && !handler) {\n  throw new Error('Provide a DistributedTransaction or a loader handler')\n}\nawait orchestrator.transactionStepResponse(key, transaction, handler)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass the storage-loader handler when only an idempotency key is available (async callback scenarios)"],"tags":["orchestration","workflow","invalid-arguments"],"backgroundTag":"missing-required-argument","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}