nopSolutions/nopCommerce · error · Exception

This shipment is already delivered

Error message

This shipment is already delivered

What it means

Thrown by OrderProcessingService.DeliverAsync when shipment.DeliveryDateUtc already has a value. It is an idempotency guard preventing a shipment from being delivered twice, which would otherwise overwrite the delivery timestamp, re-run the 'order fully delivered' shipping-status logic, and re-fire notifications.

Source

Thrown at src/Libraries/Nop.Services/Orders/OrderProcessingService.cs:2329

    /// Marks a shipment as delivered
    /// </summary>
    /// <param name="shipment">Shipment</param>
    /// <param name="notifyCustomer">True to notify customer</param>
    /// <returns>A task that represents the asynchronous operation</returns>
    public virtual async Task DeliverAsync(Shipment shipment, bool notifyCustomer)
    {
        ArgumentNullException.ThrowIfNull(shipment);

        var order = await _orderService.GetOrderByIdAsync(shipment.OrderId) ?? throw new Exception("Order cannot be loaded");

        if (!order.PickupInStore && !shipment.ShippedDateUtc.HasValue)
            throw new Exception("This shipment is not shipped yet");

        if (order.PickupInStore && !shipment.ReadyForPickupDateUtc.HasValue)
            throw new Exception("This shipment is not yet ready for pickup");

        if (shipment.DeliveryDateUtc.HasValue)
            throw new Exception("This shipment is already delivered");

        shipment.DeliveryDateUtc = DateTime.UtcNow;
        await _shipmentService.UpdateShipmentAsync(shipment);

        if (!await _orderService.HasItemsToAddToShipmentAsync(order) &&
            !await _orderService.HasItemsToShipAsync(order) &&
            !await _orderService.HasItemsToReadyForPickupAsync(order) &&
            !await _orderService.HasItemsToDeliverAsync(order))
        {
            order.ShippingStatusId = (int)ShippingStatus.Delivered;
            await _orderService.UpdateOrderAsync(order);
        }

        //add a note
        await AddOrderNoteAsync(order, $"Shipment# {shipment.Id} has been delivered");

        if (order.PickupInStore)
        {

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Guard before calling: if (shipment.DeliveryDateUtc.HasValue) return;
  2. Make the calling endpoint idempotent by keying off shipment.Id so duplicate deliver events collapse.
  3. Disable the 'Deliver' UI action once shipment.DeliveryDateUtc is set.
  4. For integrations, re-fetch the shipment right before delivery to narrow the check-then-act race.

Example fix

// before
await _orderProcessingService.DeliverAsync(shipment, true);

// after
if (shipment.DeliveryDateUtc.HasValue)
    return; // already delivered

await _orderProcessingService.DeliverAsync(shipment, true);
Defensive patterns

Strategy: validation

Validate before calling

if (shipment.DeliveryDateUtc.HasValue)
    return; // already delivered

await _orderProcessingService.DeliverAsync(shipment, true);

Try / catch

try { await _orderProcessingService.DeliverAsync(shipment, true); }
catch (NopException ex) when (ex.Message == "This shipment is already delivered")
{
    // idempotent: treat as success
}

Prevention

When it happens

Trigger: Calling DeliverAsync on a Shipment whose DeliveryDateUtc is non-null: duplicate admin 'Deliver' clicks, re-fired carrier webhooks, or concurrent requests on the same shipment.

Common situations: Store staff double-clicking Deliver; an ERP sync that reprocesses delivered shipments; a webhook retry storm after a transient HTTP failure; missing idempotency keys on a custom fulfillment endpoint.

Related errors


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/e043e0c61508477c. Report an issue: GitHub.