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
- Open the product in admin (Catalog > Products), verify the Recurring product tab: set CycleLength >= 1, select a CyclePeriod, set TotalCycles >= 1.
- Ensure all recurring items in the cart share the same cycle length and period, or split them into separate orders.
- If the product is not meant to be recurring, uncheck IsRecurring and save.
- 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
- Add an admin-panel validation on the product recurring tab that rejects CycleLength<=0 or TotalCycles<=0 on save.
- When importing or migrating products, validate recurring fields and reject rows with incomplete cycle data.
- Display recurring cycle details on the product page and cart so customers see the commitment before checkout.
- Unit test GetRecurringCycleInfoAsync with edge cases: zero cycles, mixed recurring products, recurring+non-recurring cart.
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
- Order total couldn't be calculated
- Shipping address is not provided
- Email is not valid
- Cart is empty
- {warning1};{warning2};...
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/381c4ec3de201061.
Report an issue: GitHub.