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
- Check cart.payment_collection before calling the workflow; reuse the existing collection if present
- Make the client idempotent: guard double-submits before firing the request
- 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
- Check cart.payment_collection before invoking the workflow
- Disable the pay button after first click to prevent double submission
- Model checkout steps as idempotent resumable operations
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
- Skipping refund of payment ${paymentSession.payment.id}: a l
- invalid_data
- invalid_data
- No regions found
- Variants ${variantNotFoundOrPublished.join( ", "
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/1b4441fba639f972.
Report an issue: GitHub.