Skip to content

OrderSplitter.createSellerOrders() duplicate channel key error when splitOrder returns default channelId #4631

@Draykee

Description

@Draykee

Describe the bug

OrderSplitter.createSellerOrders() unconditionally assigns both Channel({ id: partialOrder.channelId }) and the default channel to newly created seller orders. When partialOrder.channelId equals the default channel's ID, TypeORM attempts to insert the same (orderId, channelId) pair twice into the order_channels_channel join table, violating its unique primary key constraint.

The relevant code is in packages/core/src/service/helpers/order-splitter/order-splitter.ts:

const sellerOrder = await this.connection.getRepository(ctx, Order).save(
  new Order({
    // ...
    channels: [new Channel({ id: partialOrder.channelId }), defaultChannel],
    // ...
  }),
);

This affects any OrderSellerStrategy implementation where splitOrder() returns channelId equal to the default channel - which is the expected pattern when all seller/sub-orders should stay in the same channel (e.g. a marketplace where consignors don't have dedicated channels).

To Reproduce

  1. Implement a custom OrderSellerStrategy with a splitOrder() method that returns splits with channelId: ctx.channelId(the default channel)
  2. Place an order containing items that will be split into seller orders
  3. Call the addManualPaymentToOrder mutation (or any action that transitions the order to PaymentSettled)
  4. The order state transition triggers OrderSplitter.createSellerOrders() which fails with a duplicate key error

Minimal strategy to reproduce:

export class MySellerStrategy implements OrderSellerStrategy {
  async splitOrder(ctx: RequestContext, order: Order): Promise<SplitOrderContents[]> {
    // Group lines by some seller criterion
    return [
      {
        channelId: ctx.channelId, // This is the default channel
        state: 'ArrangingPayment',
        lines: order.lines,
        shippingLines: order.shippingLines,
      },
    ];
  }
}

Expected behavior

Seller orders are created successfully and assigned to the correct channel(s) without duplicate key errors, even when partialOrder.channelId matches the default channel ID.

Actual behavior

The mutation fails with:

duplicate key value violates unique constraint "PK_39853134b20afe9dfb25de18292"

Error logs

error - [TypeORM] Query error: INSERT INTO "order_channels_channel"("orderId", "channelId") VALUES ($1, $2), ($3, $4)
-- PARAMETERS: [3,1,3,1]
error - [Vendure Server] duplicate key value violates unique constraint "PK_39853134b20afe9dfb25de18292"

Environment:

  • @vendure/core version: 3.6.0
  • Nodejs version: 22
  • Database: PostgreSQL 16
  • Operating System: Windows 11
  • Package manager: npm

Workaround

Using patch-package to deduplicate the channels array before saving:

// In order-splitter.ts, change:
channels: [new Channel({ id: partialOrder.channelId }), defaultChannel],

// To:
channels: partialOrder.channelId === defaultChannel.id
  ? [defaultChannel]
  : [new Channel({ id: partialOrder.channelId }), defaultChannel],

Additional context

  • This happens consistently whenever the strategy returns the default channel ID, which is the natural approach for marketplaces that don't use per-seller channels.
  • The issue is not specific to addManualPaymentToOrder any order state transition that triggers createSellerOrders() (i.e. transitioning to PaymentSettled) will hit this bug.
  • The Vendure multivendor example plugin uses dedicated seller channels, which is why this path may not have been caught previously.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions