phacility/phabricator · warning · Exception

This order can not be voided because it is not ready for pay

Error message

This order can not be voided because it is not ready for payment.

What it means

assertCanVoidOrder() throws when an invoice cart's status is anything other than STATUS_READY: an invoice may only be voided while it is still awaiting payment. Once it has been paid (or is otherwise in flight), the correct remedy is a refund, not a void. The READY-only rule keeps the invoice lifecycle consistent.

Source

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

          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;
  }


  protected function getConfiguration() {
    return array(
      self::CONFIG_AUX_PHID => true,
      self::CONFIG_SERIALIZATION => array(
        'metadata' => self::SERIALIZATION_JSON,
      ),
      self::CONFIG_COLUMN_SCHEMA => array(
        'status' => 'text32',
        'cartClass' => 'text128',

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Check status (or wrap in canVoidOrder-style logic) and only offer void while the invoice awaits payment.
  2. If the invoice was already paid, use the refund flow instead of void.
  3. When auto-billing is enabled for a subscription, pause billing before voiding to avoid the charge/void race.
Defensive patterns

Strategy: validation

Validate before calling

if ($cart->getIsInvoice() &&
    $cart->getStatus() !== PhortuneCart::STATUS_READY) {
  // invoice no longer awaiting payment: use refund instead of void
}

Type guard

function isVoidableInvoiceCart(PhortuneCart $cart) {
  return (bool)$cart->getIsInvoice()
    && $cart->getStatus() === PhortuneCart::STATUS_READY;
}

Try / catch

try {
  $cart->assertCanVoidOrder();
} catch (Exception $ex) {
  // show $ex->getMessage(); if already paid, route the user to refund instead
}

Prevention

When it happens

Trigger: Calling assertCanVoidOrder() on an invoice cart whose status is not PhortuneCart::STATUS_READY - for example one that was already charged or is processing.

Common situations: Attempting to void an invoice after the subscription already auto-charged it; race between the billing worker charging the invoice and a manager voiding it; retrying a void on an already-voided or processed invoice.

Related errors


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