phacility/phabricator · error · Exception
This order can not be cancelled because it has already been
Error message
This order can not be cancelled because it has already been completed.
What it means
PhortuneCartImplementation::assertCanCancelOrder rejects cancellation when the cart status is STATUS_PURCHASED - a completed order must go through refunds at the charge level, not cancellation. Cancel exists for pre-purchase and holding states; once purchased, the money path is willRefundCharge/didRefundCharge via the provider.
Source
Thrown at src/applications/phortune/cart/PhortuneCartImplementation.php:30
PhabricatorUser $viewer,
array $carts);
abstract public function getName(PhortuneCart $cart);
abstract public function getCancelURI(PhortuneCart $cart);
abstract public function getDoneURI(PhortuneCart $cart);
public function getDescription(PhortuneCart $cart) {
return null;
}
public function getDoneActionName(PhortuneCart $cart) {
return pht('Return to Application');
}
public function assertCanCancelOrder(PhortuneCart $cart) {
switch ($cart->getStatus()) {
case PhortuneCart::STATUS_PURCHASED:
throw new Exception(
pht(
'This order can not be cancelled because it has already been '.
'completed.'));
break;
}
}
public function assertCanRefundOrder(PhortuneCart $cart) {
return;
}
abstract public function willCreateCart(
PhabricatorUser $viewer,
PhortuneCart $cart);
}
View on GitHub (pinned to 5720a38cfe)
Solutions
- Use the refund flow (refund charges via the provider) instead of cancelling a purchased cart
- Check cart status before offering a cancel action - only non-PURCHASED carts may be cancelled
- In custom cart implementations, implement assertCanRefundOrder() to expose a refund path for purchased carts
Example fix
// before
$implementation->assertCanCancelOrder($cart); // throws on PURCHASED
// after
if ($cart->getStatus() === PhortuneCart::STATUS_PURCHASED) {
$implementation->assertCanRefundOrder($cart);
} else {
$implementation->assertCanCancelOrder($cart);
} Defensive patterns
Strategy: validation
Validate before calling
// Route by cart status before offering the action
if ($cart->getStatus() === PhortuneCart::STATUS_PURCHASED) {
$implementation->assertCanRefundOrder($cart);
} else {
$implementation->assertCanCancelOrder($cart);
} Type guard
function canCancelCart(PhortuneCart $cart) {
return $cart->getStatus() !== PhortuneCart::STATUS_PURCHASED;
} Prevention
- Show Refund, not Cancel, once a cart reaches PURCHASED status
- Check cart status programmatically before calling assertCanCancelOrder
- Implement assertCanRefundOrder() in custom cart implementations
When it happens
Trigger: Invoking the cart cancel flow on a cart whose status is already PURCHASED; a product implementation whose UI still shows a Cancel button after checkout completes; admin scripts calling assertCanCancelOrder over historical carts.
Common situations: Buyers clicking Cancel on a completed order page; operators attempting to undo a purchase by cancelling; stale carts from testing where status moved on between page load and click.
Related errors
- This order can not be cancelled because the application has
- This order can not be cancelled because it has not been plac
- This order can not be refunded because the application has n
- This order can not be refunded because it has not been place
- This order can not be voided because it is not an invoice.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/62b04228c8d2ccb4.
Report an issue: GitHub.