nopSolutions/nopCommerce · error · NopException

recurringCyclesError

Error message

recurringCyclesError

What it means

Thrown by PrepareAndValidateRecurringShoppingAsync when IShoppingCartService.GetRecurringCycleInfoAsync returns a non-empty error string for a cart containing recurring products. The error message is whatever the shopping cart service produced (e.g. a product has an invalid recurring cycle configuration). This halts order placement before payment because the recurring billing cycle cannot be determined.

Source

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

        await PrepareAndValidateRecurringShoppingAsync(details, processPaymentRequest);

        return details;
    }

    /// <summary>
    /// Prepare and validate recurring shopping cart
    /// </summary>
    /// <param name="details">PlaceOrder container</param>
    /// <param name="processPaymentRequest">payment info holder</param>
    /// <returns>A task that represents the asynchronous operation</returns>
    /// <exception cref="NopException">Validation problems</exception>
    protected virtual async Task PrepareAndValidateRecurringShoppingAsync(PlaceOrderContainer details, ProcessPaymentRequest processPaymentRequest)
    {
        var (recurringCyclesError, recurringCycleLength, recurringCyclePeriod, recurringTotalCycles) = await _shoppingCartService.GetRecurringCycleInfoAsync(details.Cart);

        if (!string.IsNullOrEmpty(recurringCyclesError))
            throw new NopException(recurringCyclesError);

        processPaymentRequest.RecurringCycleLength = recurringCycleLength;
        processPaymentRequest.RecurringCyclePeriod = recurringCyclePeriod;
        processPaymentRequest.RecurringTotalCycles = recurringTotalCycles;
    }

    /// <summary>
    /// Prepare and validate all totals
    ///
    /// sub total, shipping total, payment total, tax amount etc.
    /// </summary>
    /// <param name="details">PlaceOrder container</param>
    /// <param name="processPaymentRequest">payment info holder</param>
    /// <returns>A task that represents the asynchronous operation</returns>
    /// <exception cref="NopException">Validation problems</exception>
    protected virtual async Task PrepareAndValidateTotalsAsync(PlaceOrderContainer details, ProcessPaymentRequest processPaymentRequest)
    {
        var (discountAmountInclTax, discountAmountExclTax, appliedDiscounts, subTotalWithoutDiscountInclTax,

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Open the product in admin (Catalog > Products), verify the Recurring product tab: set CycleLength >= 1, select a CyclePeriod, set TotalCycles >= 1.
  2. Ensure all recurring items in the cart share the same cycle length and period, or split them into separate orders.
  3. If the product is not meant to be recurring, uncheck IsRecurring and save.
  4. Check GetRecurringCycleInfoAsync in ShoppingCartService to see the exact validation that generated the error string.

Example fix

// before (admin misconfiguration): product.CycleLength = 0, product.TotalCycles = 0
// after: product.CycleLength = 30; product.CyclePeriod = CyclePeriod.Days; product.TotalCycles = 12;
Defensive patterns

Strategy: validation

Validate before calling

// Before PlaceOrder, validate recurring products in the cart
var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, storeId);
foreach (var sci in cart)
{
    var product = await _productService.GetProductByIdAsync(sci.ProductId);
    if (product.IsRecurring && (product.RecurringCycleLength <= 0 || product.RecurringTotalCycles <= 0))
    {
        // surface a user-friendly warning before reaching PlaceOrder
        warnings.Add($"Product '{product.Name}' has invalid recurring cycle settings.");
    }
}

Type guard

bool HasValidRecurringConfig(Product p) =>
    !p.IsRecurring || (p.RecurringCycleLength > 0 && p.RecurringTotalCycles > 0 && p.RecurringCyclePeriod != CyclePeriod.NotSet);

Try / catch

try
{
    var result = await _orderProcessingService.PlaceOrderAsync(processPaymentRequest);
}
catch (NopException ex) when (ex.Message.Contains("recurring"))
{
    // redirect customer to cart with a recurring-config warning
    _notificationService.ErrorNotification(ex.Message);
    return RedirectToRoute("ShoppingCart");
}

Prevention

When it happens

Trigger: A cart contains at least one product marked as recurring (IsRecurring=true), and GetRecurringCycleInfoAsync detects a misconfiguration such as CycleLength<=0, CyclePeriod unset, or TotalCycles<=0 on the product. Also triggered when multiple recurring items in the cart have inconsistent cycle definitions that cannot be merged.

Common situations: Admin sets a product to recurring but leaves CycleLength or TotalCycles at 0. A product's recurring settings were partially edited in the admin panel. A new recurring product was imported via data migration with incomplete cycle fields. Mixed recurring and non-recurring items, or two recurring items with different cycles in the same cart.

Related errors


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