medusajs/medusa · error · Error

Incorrect action type.

Error message

Incorrect action type.

What it means

A responseIdempotencyKey's last segment encodes the handler type (invoke vs compensate). The orchestrator checks it against the step's current mode: a compensating step must receive COMPENSATE responses, an invoking step INVOKE. A mismatch throws this plain Error, protecting step state from wrong-lifecycle updates.

Solutions

  1. Always use the idempotency key exactly as handed to your step (it encodes the correct actionType)
  2. When handling async callbacks, catch this and treat late/incorrect-lifecycle responses as no-ops (log and ack)
  3. If a response legitimately arrives during compensation, respond through the compensation path or re-run the workflow

Example fix

// before
const key = `${modelId}:${txId}:${action}:invoke` // hardcoded type

// after
// echo back the key the engine generated for this step invocation
await orchestrator.transactionStepResponse(step.context.idempotencyKey)
Defensive patterns

Strategy: try-catch

Validate before calling

const [, , action, actionType] = key.split(':')
const expected = step.isCompensating() ? 'compensate' : 'invoke'
if (actionType !== expected) {
  return // late/out-of-lifecycle response; ignore
}

Try / catch

try { await orchestrator.transactionStepResponse(key, null, handler) } catch (e) { if (/Incorrect action type/.test(e.message)) { /* late async response during compensation; ack */ return } throw e }

Prevention

When it happens

Trigger: Submitting a response whose key ends in :invoke for a step that is currently compensating (or :compensate for an invoking step). Typically from reusing/deriving keys manually, or an async response arriving after the flow already began compensating.

Common situations: Webhook for an async step arriving after the workflow started rolling back; constructing keys from the wrong actionType constant; duplicated callback processing with stale keys.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/a9dfc8b165da7493. Report an issue: GitHub.

Appendix: source

Thrown at packages/core/orchestration/src/transaction/transaction-orchestrator.ts:1864

        undefined,
        existingTransaction.errors,
        existingTransaction.context
      )
    }

    const step = TransactionOrchestrator.getStepByAction(
      transaction.getFlow(),
      action
    )

    if (step === null) {
      throw new Error("Action not found.")
    } else if (
      step.isCompensating()
        ? actionType !== TransactionHandlerType.COMPENSATE
        : actionType !== TransactionHandlerType.INVOKE
    ) {
      throw new Error("Incorrect action type.")
    }
    return [transaction, step]
  }

  /** Skip the execution of a specific transaction and step
   * @param responseIdempotencyKey - The idempotency key for the step
   * @param handler - The handler function to execute the step
   * @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
   */
  public async skipStep({
    responseIdempotencyKey,
    handler,
    transaction,
  }: {
    responseIdempotencyKey: string
    handler?: TransactionStepHandler
    transaction?: DistributedTransactionType
  }): Promise<DistributedTransactionType> {

View on GitHub (pinned to 5e06e544a2)