{"record":{"id":"bdf192b41af66735","repo":"medusajs/medusa","slug":"transaction-transactionid-could-not-be-found","errorCode":null,"errorMessage":"Transaction ${transactionId} could not be found.","messagePattern":"Transaction (.+?) could not be found\\.","errorType":"exception","errorClass":"MedusaError","httpStatus":null,"severity":"error","filePath":"packages/core/orchestration/src/transaction/transaction-orchestrator.ts","lineNumber":1786,"sourceCode":"\n  /** Returns an existing transaction\n   * @param transactionId - unique identifier of the transaction\n   * @param handler - function to handle action of the transaction\n   */\n  public async retrieveExistingTransaction(\n    transactionId: string,\n    handler: TransactionStepHandler,\n    options?: { isCancelling?: boolean }\n  ): Promise<DistributedTransactionType> {\n    const existingTransaction =\n      await TransactionOrchestrator.loadTransactionById(\n        this.id,\n        transactionId,\n        { isCancelling: options?.isCancelling }\n      )\n\n    if (!existingTransaction) {\n      throw new MedusaError(\n        MedusaError.Types.NOT_FOUND,\n        `Transaction ${transactionId} could not be found.`\n      )\n    }\n\n    const transaction = new DistributedTransaction(\n      existingTransaction.flow,\n      handler,\n      undefined,\n      existingTransaction?.errors,\n      existingTransaction?.context\n    )\n\n    return transaction\n  }\n\n  private static getStepByAction(\n    flow: TransactionFlow,","sourceCodeStart":1768,"sourceCodeEnd":1804,"githubUrl":"https://github.com/medusajs/medusa/blob/5e06e544a296b9033f20f71f11c559f81a0e5739/packages/core/orchestration/src/transaction/transaction-orchestrator.ts#L1768-L1804","documentation":"TransactionOrchestrator.loadTransactionById (used by resume/cancel/retry paths) retrieves the persisted transaction by model id and transaction id from storage. If storage returns nothing, NOT_FOUND is thrown — the transaction was never checkpointed, was already cleaned up, or the ids are wrong.","triggerScenarios":"Calling resume/cancel/skip with a transactionId that does not exist in the configured workflow storage, or after the execution record expired/was purged, or when the storage is empty (wrong Redis DB / different storage configured than the one that ran the workflow).","commonSituations":"Restarting the app with a fresh/in-memory workflow storage and trying to resume pre-restart transactions; pointing at a different Redis/database than where the workflow ran; typo'd or truncated transactionId (URL decoding issues).","solutions":["Verify the transactionId and modelId exactly match what was returned when the workflow started","Ensure the same workflow storage backend/instance is used across restarts (persistent Redis/DB, not memory)","Check retention: if executions expire or clearExpiredExecutions purged them, the transaction is gone — restart the workflow instead of resuming"],"exampleFix":"// before\nawait orchestrator.resume(txId) // throws if never persisted\n\n// after\nconst existing = await orchestrator.retrieveExistingTransaction(txId).catch(() => null)\nif (!existing) {\n  // start fresh instead of resuming\n  await orchestrator.run(...)\n} else {\n  await orchestrator.resume(existing)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await orchestrator.resume(txId) } catch (e) { if (e.type === 'not_found' && /could not be found/.test(e.message)) { await orchestrator.run(/* fresh */) } else throw e }","preventionTips":["Use persistent workflow storage (Redis/DB) so transactions survive restarts","Persist the transactionId returned at workflow start before attempting resume"],"tags":["orchestration","workflow","not-found","persistence"],"backgroundTag":"transaction-not-found","analyzedSha":"5e06e544a296b9033f20f71f11c559f81a0e5739","analyzedAt":"2026-08-27T07:24:39.599Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}