medusajs/medusa · error · Error

Cart ${cart.id} already has a payment collection

Error message

Cart ${cart.id} already has a payment collection

What it means

createPaymentCollectionForCartWorkflow guards against duplicates: if the cart already has an attached payment_collection, the validation step throws a plain Error (not a MedusaError) instead of creating a second collection.

Source

Thrown at packages/core/core-flows/src/cart/workflows/create-payment-collection-for-cart.ts:56

 *
 * :::
 *
 * @example
 * const data = validateExistingPaymentCollectionStep({
 *   cart: {
 *     // other cart details...
 *     payment_collection: {
 *       id: "paycol_123",
 *       // other payment collection details.
 *     }
 *   }
 * })
 */
export const validateExistingPaymentCollectionStep = createStep(
  "validate-existing-payment-collection",
  ({ cart }: ValidateExistingPaymentCollectionStepInput) => {
    if (cart.payment_collection) {
      throw new Error(`Cart ${cart.id} already has a payment collection`)
    }
  }
)

export const createPaymentCollectionForCartWorkflowId =
  "create-payment-collection-for-cart"
/**
 * This workflow creates a payment collection for a cart. It's executed by the
 * [Create Payment Collection Store API Route](https://docs.medusajs.com/api/store/payment-collections/create-payment-collection).
 *
 * You can use this workflow within your own customizations or custom workflows, allowing you to wrap custom logic around adding creating a payment collection for a cart.
 *
 * @example
 * const { result } = await createPaymentCollectionForCartWorkflow(container)
 * .run({
 *   input: {
 *     cart_id: "cart_123",
 *     metadata: {

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Check cart.payment_collection before calling the workflow; reuse the existing collection if present
  2. Make the client idempotent: guard double-submits before firing the request
  3. If the stale collection is unwanted, delete it (or reset payment sessions) before creating a new one

Example fix

// before
await createPaymentCollectionForCartWorkflow(container).run({ input: { cart_id } })

// after
const cart = await query.graph({ entity: 'cart', filters: { id: cart_id }, fields: ['payment_collection.id'] })
if (!cart.data[0].payment_collection) {
  await createPaymentCollectionForCartWorkflow(container).run({ input: { cart_id } })
}
Defensive patterns

Strategy: type-guard

Validate before calling

const { data: [cart] } = await query.graph({ entity: 'cart', fields: ['payment_collection.id'], filters: { id: cart_id } })
if (cart.payment_collection) {
  return { payment_collection: cart.payment_collection }
}

Type guard

const cartNeedsPaymentCollection = (cart: CartDTO) =>
  cart.payment_collection == null

Try / catch

try {
  await createPaymentCollectionForCartWorkflow(container).run({ input: { cart_id } })
} catch (e) {
  if (/already has a payment collection/.test(e.message)) {
    // refetch cart and continue with the existing collection
  }
}

Prevention

When it happens

Trigger: Calling createPaymentCollectionForCart for a cart that already went through payment creation — e.g. re-entering checkout, retrying after a network error, or calling the workflow twice in a flow without checking prior state.

Common situations: Storefront checkout resumed after refresh/retry; parallel requests (double click on 'Pay'); custom flows that assume the workflow is idempotent when it is not.

Related errors


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