{"record":{"id":"e134935a0b701e79","repo":"nopSolutions/nopCommerce","slug":"cannot-do-cancel-for-order","errorCode":null,"errorMessage":"Cannot do cancel for order.","messagePattern":"Cannot do cancel for order\\.","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Libraries/Nop.Services/Orders/OrderProcessingService.cs","lineNumber":2394,"sourceCode":"\r\n        if (order.OrderStatus == OrderStatus.Cancelled)\r\n            return false;\r\n\r\n        return true;\r\n    }\r\n\r\n    /// <summary>\r\n    /// Cancels order\r\n    /// </summary>\r\n    /// <param name=\"order\">Order</param>\r\n    /// <param name=\"notifyCustomer\">True to notify customer</param>\r\n    /// <returns>A task that represents the asynchronous operation</returns>\r\n    public virtual async Task CancelOrderAsync(Order order, bool notifyCustomer)\r\n    {\r\n        ArgumentNullException.ThrowIfNull(order);\r\n\r\n        if (!CanCancelOrder(order))\r\n            throw new NopException(\"Cannot do cancel for order.\");\r\n\r\n        //cancel order\r\n        await SetOrderStatusAsync(order, OrderStatus.Cancelled, notifyCustomer);\r\n\r\n        //notify store owner\r\n        var currentCustomer = await _workContext.GetCurrentCustomerAsync();\r\n        if (order.CustomerId == currentCustomer.Id)\r\n            await _workflowMessageService.SendOrderCancelledStoreOwnerNotificationAsync(order, _localizationSettings.DefaultAdminLanguageId);\r\n\r\n        //add a note\r\n        await AddOrderNoteAsync(order, \"Order has been cancelled\");\r\n\r\n        //return (add) back redeemded reward points\r\n        await ReturnBackRedeemedRewardPointsAsync(order);\r\n\r\n        //delete gift card usage history\r\n        if (_orderSettings.DeleteGiftCardUsageHistory)\r\n            await _giftCardService.DeleteGiftCardUsageHistoryAsync(order);\r","sourceCodeStart":2376,"sourceCodeEnd":2412,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Libraries/Nop.Services/Orders/OrderProcessingService.cs#L2376-L2412","documentation":"Thrown by OrderProcessingService.CancelOrderAsync when CanCancelOrder(order) returns false. CanCancelOrder returns false only when order.OrderStatus == OrderStatus.Cancelled, so this is an idempotency guard: you cannot cancel an order that is already cancelled. Cancellation triggers inventory return, reward-point return, gift-card history deletion, and recurring-payment cancellation, all of which must not run twice.","triggerScenarios":"Calling CancelOrderAsync on an Order whose OrderStatus is already Cancelled. Happens on duplicate cancel requests, retries, or an admin re-clicking Cancel after the status flipped.","commonSituations":"Admin UI double-click; an order-management integration that re-sends cancel events; a background job that cancels orders and re-runs after a failure; concurrent cancel requests.","solutions":["Guard with the public helper: if (!_orderProcessingService.CanCancelOrder(order)) return; or check order.OrderStatus != OrderStatus.Cancelled before calling.","Make the cancel endpoint idempotent (key off order.Id) so duplicate events are no-ops.","Disable the 'Cancel' UI action when order.OrderStatus == Cancelled.","In integrations, fetch the order fresh right before cancelling to reduce the race window."],"exampleFix":"// before\nawait _orderProcessingService.CancelOrderAsync(order, true);\n\n// after\nif (!_orderProcessingService.CanCancelOrder(order))\n    return;\n\nawait _orderProcessingService.CancelOrderAsync(order, true);","handlingStrategy":"validation","validationCode":"if (!_orderProcessingService.CanCancelOrder(order))\n    return;\n\nawait _orderProcessingService.CancelOrderAsync(order, true);","typeGuard":null,"tryCatchPattern":"try { await _orderProcessingService.CancelOrderAsync(order, true); }\ncatch (NopException ex) when (ex.Message == \"Cannot do cancel for order.\")\n{\n    // already cancelled; treat as no-op\n}","preventionTips":["Disable the 'Cancel' UI action when order.OrderStatus == Cancelled.","Make the cancel endpoint idempotent, keyed on order.Id.","Fetch the order fresh before cancelling to narrow the race."],"tags":["order","cancel","idempotency","order-processing"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}