phacility/phabricator · warning · Exception

This order can not be voided because it is not an invoice.

Error message

This order can not be voided because it is not an invoice.

What it means

assertCanVoidOrder() throws when getIsInvoice() is false: voiding is reserved for subscription-generated invoices, because a void marks an issued invoice as never-payable. Ordinary orders do not go through voiding - they are cancelled (before purchase) or refunded (after charge).

Source

Thrown at src/applications/phortune/storage/PhortuneCart.php:545

  public function assertCanRefundOrder() {
    switch ($this->getStatus()) {
      case self::STATUS_BUILDING:
        throw new Exception(
          pht(
            'This order can not be refunded because the application has not '.
            'finished building it yet.'));
      case self::STATUS_READY:
        throw new Exception(
          pht(
            'This order can not be refunded because it has not been placed.'));
    }

    return $this->getImplementation()->assertCanRefundOrder($this);
  }

  public function assertCanVoidOrder() {
    if (!$this->getIsInvoice()) {
      throw new Exception(
        pht(
          'This order can not be voided because it is not an invoice.'));
    }

    switch ($this->getStatus()) {
      case self::STATUS_READY:
        break;
      default:
        throw new Exception(
          pht(
            'This order can not be voided because it is not ready for '.
            'payment.'));
    }

    return null;
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Check $cart->getIsInvoice() before offering the void action.
  2. For non-invoice orders use cancel (unplaced) or refund (charged) instead.
  3. Filter lists so void only appears on subscription invoice carts.

Example fix

// before
$cart->assertCanVoidOrder(); // throws 'This order can not be voided because it is not an invoice.'

// after
if ($cart->getIsInvoice()) {
  $cart->assertCanVoidOrder();
  // proceed with void
} else {
  // use canCancelOrder()/canRefundOrder() paths for normal orders
}
Defensive patterns

Strategy: validation

Validate before calling

if (!$cart->getIsInvoice()) {
  // void applies only to subscription invoices; use cancel or refund instead
  return $this->newDialog()->setTitle(pht('Not an Invoice'));
}

Type guard

function isInvoiceCart(PhortuneCart $cart) {
  return (bool)$cart->getIsInvoice();
}

Prevention

When it happens

Trigger: Calling assertCanVoidOrder() on a cart created by a normal purchase flow (isInvoice is 0/absent), e.g. pointing a void action at a user-initiated order instead of a subscription invoice.

Common situations: Generic order-management tooling applying the void action to all carts; confusion between void (invoice-only), cancel, and refund in custom controllers.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/28788ef2d7e5a847. Report an issue: GitHub.