nopSolutions/nopCommerce · critical · NopException

Customer could not be loaded

Error message

Customer could not be loaded

What it means

Thrown in ProcessNextRecurringPaymentAsync when _customerService.GetCustomerByIdAsync(initialOrder.CustomerId) returns null. The recurring payment's owning customer no longer exists, so nopCommerce cannot build the ProcessPaymentRequest (which needs CustomerId) and refuses the next cycle.

Source

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

    /// <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
            };

            processPaymentRequest.CustomValues.FillByXml(initialOrder.CustomValuesXml);

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Cancel (IsActive=false) any recurring payments before deleting a customer.
  2. Restore the customer record from backup if the deletion was accidental.
  3. Extend customer-deletion logic to deactivate dependent recurring payments.

Example fix

// before
await _customerService.DeleteCustomerAsync(customer);

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

Strategy: validation

Validate before calling

var initialOrder = await _orderService.GetOrderByIdAsync(recurringPayment.InitialOrderId);
var customer = initialOrder is not null
    ? await _customerService.GetCustomerByIdAsync(initialOrder.CustomerId)
    : null;

if (customer is null)
{
    recurringPayment.IsActive = false;
    await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
    _logger.LogWarning("Customer missing for recurring payment {Id}", recurringPayment.Id);
    return Array.Empty<string>();
}

return await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);

Type guard

async Task<bool> RecurringCustomerExistsAsync(RecurringPayment rp)
{
    var order = await _orderService.GetOrderByIdAsync(rp.InitialOrderId);
    return order is not null
        && await _customerService.GetCustomerByIdAsync(order.CustomerId) is not null;
}

Try / catch

try
{
    var errors = await _orderProcessingService.ProcessNextRecurringPaymentAsync(recurringPayment);
}
catch (NopException ex) when (ex.Message == "Customer could not be loaded")
{
    recurringPayment.IsActive = false;
    await _orderService.UpdateRecurringPaymentAsync(recurringPayment);
}

Prevention

When it happens

Trigger: The customer account was deleted (admin purge of inactive/guest accounts) while a recurring payment referencing their initial order is still active. Also possible after GDPR delete-me processing.

Common situations: Customer deletion routine that does not cancel their recurring payments; GDPR right-to-erasure fulfilment that removed the customer but left subscriptions running; guest-customer cleanup.

Related errors


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