nopSolutions/nopCommerce · critical · NopException

Initial order could not be loaded

Error message

Initial order could not be loaded

What it means

Thrown in ProcessNextRecurringPaymentAsync when _orderService.GetOrderByIdAsync(recurringPayment.InitialOrderId) returns null. The recurring payment references a seed order that no longer exists in the database, so nopCommerce cannot reconstruct the payment context (customer, items, totals) needed for the next cycle.

Source

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

    /// Process next recurring payment
    /// </summary>
    /// <param name="recurringPayment">Recurring payment</param>
    /// <param name="paymentResult">Process payment result (info about last payment for automatic recurring payments)</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the collection of errors
    /// </returns>
    public virtual async Task<IEnumerable<string>> ProcessNextRecurringPaymentAsync(RecurringPayment recurringPayment, ProcessPaymentResult paymentResult = null)
    {
        ArgumentNullException.ThrowIfNull(recurringPayment);

        try
        {
            if (!recurringPayment.IsActive)
                throw new NopException("Recurring payment is not active");

            var initialOrder = await _orderService.GetOrderByIdAsync(recurringPayment.InitialOrderId)
                               ?? throw new NopException("Initial order could not be loaded");

            var customer = await _customerService.GetCustomerByIdAsync(initialOrder.CustomerId)
                           ?? throw new NopException("Customer could not be loaded");

            if (await GetNextPaymentDateAsync(recurringPayment) is null)
                throw new NopException("Next payment date could not be calculated");

            //payment info
            var processPaymentRequest = new ProcessPaymentRequest
            {
                StoreId = initialOrder.StoreId,
                CustomerId = customer.Id,
                OrderGuid = Guid.NewGuid(),
                InitialOrder = initialOrder,
                RecurringCycleLength = recurringPayment.CycleLength,
                RecurringCyclePeriod = recurringPayment.CyclePeriod,
                RecurringTotalCycles = recurringPayment.TotalCycles
            };

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Restore the missing initial order from backup, or hard-delete the orphaned RecurringPayment record if the subscription is defunct.
  2. Audit order-deletion routines to cascade-clean RecurringPayment rows.
  3. Add a FK/data-integrity check so recurring payments cannot outlive their initial order.

Example fix

// before
await _orderService.DeleteOrderAsync(initialOrder); // leaves orphan recurring payment

// after
foreach (var rp in await _orderService.GetRecurringPaymentsByInitialOrderIdAsync(initialOrder.Id))
    rp.IsActive = false;
await _orderService.UpdateRecurringPaymentAsync(rp);
await _orderService.DeleteOrderAsync(initialOrder);
Defensive patterns

Strategy: validation

Validate before calling

var initialOrder = await _orderService.GetOrderByIdAsync(recurringPayment.InitialOrderId);
if (initialOrder is null)
{
    recurringPayment.IsActive = false; // orphaned - quarantine
    await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
    _logger.LogWarning("Recurring payment {Id} references deleted initial order {OrderId}",
        recurringPayment.Id, recurringPayment.InitialOrderId);
    return Array.Empty<string>();
}

return await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);

Type guard

async Task<bool> InitialOrderExistsAsync(RecurringPayment rp)
    => await _orderService.GetOrderByIdAsync(rp.InitialOrderId) is not null;

Try / catch

try
{
    var errors = await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);
}
catch (NopException ex) when (ex.Message == "Initial order could not be loaded")
{
    recurringPayment.IsActive = false;
    await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
    _logger.LogCritical(ex, "Orphaned recurring payment {Id}", recurringPayment.Id);
}

Prevention

When it happens

Trigger: The initial/seed order was hard-deleted from the Orders table while its RecurringPayment row remains; data corruption; restore from backup that missed the order; manual SQL deletion of orders without cleaning recurring payment records.

Common situations: Aggressive order cleanup scripts that delete orders but not related recurring payments; partial DB restore; buggy custom order-purge logic.

Related errors


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