phacility/phabricator · error · PhabricatorWorkerPermanentFailureException

Subscription is missing billing period information.

Error message

Subscription is missing billing period information.

What it means

The worker derives the billing window from the trigger.last-epoch and trigger.this-epoch keys that the scheduler writes into task data; last-epoch falls back to the subscription's creation date, so in practice this error means trigger.this-epoch is absent. The task was queued without normal trigger metadata and permanently fails, because the billing window cannot be reconstructed.

Source

Thrown at src/applications/phortune/worker/PhortuneSubscriptionWorker.php:241

  /**
   * Get the start and end epoch timestamps for this billing period.
   *
   * @param PhortuneSubscription The subscription being billed.
   * @return pair<int, int> Beginning and end of the billing range.
   */
  private function getBillingPeriodRange(PhortuneSubscription $subscription) {
    $data = $this->getTaskData();

    $last_epoch = idx($data, 'trigger.last-epoch');
    if (!$last_epoch) {
      // If this is the first time the subscription is firing, use the
      // creation date as the start of the billing period.
      $last_epoch = $subscription->getDateCreated();
    }
    $this_epoch = idx($data, 'trigger.this-epoch');

    if (!$last_epoch || !$this_epoch) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht('Subscription is missing billing period information.'));
    }

    $period_length = ($this_epoch - $last_epoch);
    if ($period_length <= 0) {
      throw new PhabricatorWorkerPermanentFailureException(
        pht(
          'Subscription has invalid billing period.'));
    }

    if (empty($data['manual'])) {
      if (PhabricatorTime::getNow() < $this_epoch) {
        throw new Exception(
          pht(
            'Refusing to generate a subscription invoice for a billing period '.
            'which ends in the future.'));
      }
    }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Do not hand-fire the worker: let the subscription's own trigger schedule it, which writes trigger.last-epoch and trigger.this-epoch automatically
  2. Inspect the failed task's data JSON in the daemon task detail view to see which epoch key is missing
  3. Re-save the subscription's billing settings to rebuild a clean trigger with fresh epoch data
Defensive patterns

Strategy: validation

Validate before calling

// When queueing the subscription worker, always include trigger epochs.
if (empty($data['trigger.this-epoch']) || empty($data['trigger.last-epoch'])) {
  throw new Exception(pht('Refusing to queue subscription task without epochs.'));
}

Prevention

When it happens

Trigger: Manually queueing PhortuneSubscriptionWorker tasks without epoch data; code that clones or re-queues subscription tasks and drops the trigger.* keys; corrupted task data after an upgrade or partial import.

Common situations: Custom admin scripts that fire subscription billing by hand instead of relying on the trigger; debugging copies of production tasks; tooling that reconstructs task data from scratch.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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