{"record":{"id":"385a578a1b13cf93","repo":"phacility/phabricator","slug":"subscription-is-missing-billing-period-information","errorCode":null,"errorMessage":"Subscription is missing billing period information.","messagePattern":"Subscription is missing billing period information\\.","errorType":"exception","errorClass":"PhabricatorWorkerPermanentFailureException","httpStatus":null,"severity":"error","filePath":"src/applications/phortune/worker/PhortuneSubscriptionWorker.php","lineNumber":241,"sourceCode":"  /**\n   * Get the start and end epoch timestamps for this billing period.\n   *\n   * @param PhortuneSubscription The subscription being billed.\n   * @return pair<int, int> Beginning and end of the billing range.\n   */\n  private function getBillingPeriodRange(PhortuneSubscription $subscription) {\n    $data = $this->getTaskData();\n\n    $last_epoch = idx($data, 'trigger.last-epoch');\n    if (!$last_epoch) {\n      // If this is the first time the subscription is firing, use the\n      // creation date as the start of the billing period.\n      $last_epoch = $subscription->getDateCreated();\n    }\n    $this_epoch = idx($data, 'trigger.this-epoch');\n\n    if (!$last_epoch || !$this_epoch) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht('Subscription is missing billing period information.'));\n    }\n\n    $period_length = ($this_epoch - $last_epoch);\n    if ($period_length <= 0) {\n      throw new PhabricatorWorkerPermanentFailureException(\n        pht(\n          'Subscription has invalid billing period.'));\n    }\n\n    if (empty($data['manual'])) {\n      if (PhabricatorTime::getNow() < $this_epoch) {\n        throw new Exception(\n          pht(\n            'Refusing to generate a subscription invoice for a billing period '.\n            'which ends in the future.'));\n      }\n    }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/worker/PhortuneSubscriptionWorker.php#L223-L259","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not hand-fire the worker: let the subscription's own trigger schedule it, which writes trigger.last-epoch and trigger.this-epoch automatically","Inspect the failed task's data JSON in the daemon task detail view to see which epoch key is missing","Re-save the subscription's billing settings to rebuild a clean trigger with fresh epoch data"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// When queueing the subscription worker, always include trigger epochs.\nif (empty($data['trigger.this-epoch']) || empty($data['trigger.last-epoch'])) {\n  throw new Exception(pht('Refusing to queue subscription task without epochs.'));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-fire PhortuneSubscriptionWorker; rely on the subscription trigger that writes epoch data","When cloning tasks, copy the data array verbatim rather than rebuilding it","Log task data at queue time so missing keys are visible in failure forensics"],"tags":["phabricator","phortune","subscription","worker","task-data","epoch"],"backgroundTag":"missing-required-field","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}