{"record":{"id":"381c4ec3de201061","repo":"nopSolutions/nopCommerce","slug":"recurringcycleserror","errorCode":null,"errorMessage":"recurringCyclesError","messagePattern":"recurringCyclesError","errorType":"validation","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Libraries/Nop.Services/Orders/OrderProcessingService.cs","lineNumber":329,"sourceCode":"\r\n        await PrepareAndValidateRecurringShoppingAsync(details, processPaymentRequest);\r\n\r\n        return details;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Prepare and validate recurring shopping cart\r\n    /// </summary>\r\n    /// <param name=\"details\">PlaceOrder container</param>\r\n    /// <param name=\"processPaymentRequest\">payment info holder</param>\r\n    /// <returns>A task that represents the asynchronous operation</returns>\r\n    /// <exception cref=\"NopException\">Validation problems</exception>\r\n    protected virtual async Task PrepareAndValidateRecurringShoppingAsync(PlaceOrderContainer details, ProcessPaymentRequest processPaymentRequest)\r\n    {\r\n        var (recurringCyclesError, recurringCycleLength, recurringCyclePeriod, recurringTotalCycles) = await _shoppingCartService.GetRecurringCycleInfoAsync(details.Cart);\r\n\r\n        if (!string.IsNullOrEmpty(recurringCyclesError))\r\n            throw new NopException(recurringCyclesError);\r\n\r\n        processPaymentRequest.RecurringCycleLength = recurringCycleLength;\r\n        processPaymentRequest.RecurringCyclePeriod = recurringCyclePeriod;\r\n        processPaymentRequest.RecurringTotalCycles = recurringTotalCycles;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Prepare and validate all totals\r\n    ///\r\n    /// sub total, shipping total, payment total, tax amount etc.\r\n    /// </summary>\r\n    /// <param name=\"details\">PlaceOrder container</param>\r\n    /// <param name=\"processPaymentRequest\">payment info holder</param>\r\n    /// <returns>A task that represents the asynchronous operation</returns>\r\n    /// <exception cref=\"NopException\">Validation problems</exception>\r\n    protected virtual async Task PrepareAndValidateTotalsAsync(PlaceOrderContainer details, ProcessPaymentRequest processPaymentRequest)\r\n    {\r\n        var (discountAmountInclTax, discountAmountExclTax, appliedDiscounts, subTotalWithoutDiscountInclTax,\r","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Libraries/Nop.Services/Orders/OrderProcessingService.cs#L311-L347","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (admin misconfiguration): product.CycleLength = 0, product.TotalCycles = 0\n// after: product.CycleLength = 30; product.CyclePeriod = CyclePeriod.Days; product.TotalCycles = 12;","handlingStrategy":"validation","validationCode":"// Before PlaceOrder, validate recurring products in the cart\nvar cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, storeId);\nforeach (var sci in cart)\n{\n    var product = await _productService.GetProductByIdAsync(sci.ProductId);\n    if (product.IsRecurring && (product.RecurringCycleLength <= 0 || product.RecurringTotalCycles <= 0))\n    {\n        // surface a user-friendly warning before reaching PlaceOrder\n        warnings.Add($\"Product '{product.Name}' has invalid recurring cycle settings.\");\n    }\n}","typeGuard":"bool HasValidRecurringConfig(Product p) =>\n    !p.IsRecurring || (p.RecurringCycleLength > 0 && p.RecurringTotalCycles > 0 && p.RecurringCyclePeriod != CyclePeriod.NotSet);","tryCatchPattern":"try\n{\n    var result = await _orderProcessingService.PlaceOrderAsync(processPaymentRequest);\n}\ncatch (NopException ex) when (ex.Message.Contains(\"recurring\"))\n{\n    // redirect customer to cart with a recurring-config warning\n    _notificationService.ErrorNotification(ex.Message);\n    return RedirectToRoute(\"ShoppingCart\");\n}","preventionTips":["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."],"tags":["nopcommerce","recurring-payment","product-config","validation","order-processing"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}