{"record":{"id":"7f6c114bfeef839d","repo":"phacility/phabricator","slug":"when-specifying-s-or-s-you-must-specify-both-ar","errorCode":null,"errorMessage":"When specifying %s or %s, you must specify both arguments to define the beginning and end of the billing range.","messagePattern":"When specifying (.+?) or (.+?), you must specify both arguments to define the beginning and end of the billing range\\.","errorType":"console","errorClass":"PhutilArgumentUsageException","httpStatus":null,"severity":"warning","filePath":"src/applications/phortune/management/PhabricatorPhortuneManagementInvoiceWorkflow.php","lineNumber":99,"sourceCode":"    $console->writeOut(\n      \"%s\\n\",\n      pht(\n        'Set current time to %s.',\n        phabricator_datetime(PhabricatorTime::getNow(), $viewer)));\n\n    $auto_range = $args->getArg('auto-range');\n    $last_arg = $args->getArg('last');\n    $next_arg = $args->getArg('next');\n\n    if (!$auto_range && !$last_arg && !$next_arg) {\n      throw new PhutilArgumentUsageException(\n        pht(\n          'Specify a billing range with %s and %s, or use %s.',\n          '--last',\n          '--next',\n          '--auto-range'));\n    } else if (!$auto_range & (!$last_arg || !$next_arg)) {\n      throw new PhutilArgumentUsageException(\n        pht(\n          'When specifying %s or %s, you must specify both arguments '.\n          'to define the beginning and end of the billing range.',\n          '--last',\n          '--next'));\n    } else if (!$auto_range && ($last_arg && $next_arg)) {\n      $last_time = $this->parseTimeArgument($args->getArg('last'));\n      $next_time = $this->parseTimeArgument($args->getArg('next'));\n    } else if ($auto_range && ($last_arg || $next_arg)) {\n      throw new PhutilArgumentUsageException(\n        pht(\n          'Use either %s or %s and %s to specify the '.\n          'billing range, but not both.',\n          '--auto-range',\n          '--last',\n          '--next'));\n    } else {\n      $trigger = $subscription->getTrigger();","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/management/PhabricatorPhortuneManagementInvoiceWorkflow.php#L81-L117","documentation":"PhutilArgumentUsageException thrown by the phortune invoice workflow when exactly one of --last / --next is given without --auto-range. A billing range is a closed interval: --last is the beginning epoch and --next the end epoch, and supplying only one half is ambiguous, so the workflow refuses rather than invent the missing bound (an invented bound risks double-billing).","triggerScenarios":"Running `... --subscription <PHID> --last 1690000000` (no --next), or `... --next 1692600000` (no --last), each without --auto-range. The condition (!$auto_range && (!$last_arg || !$next_arg)) fires when either half is missing.","commonSituations":"Scripts templating only the 'since' epoch; operators assuming --next defaults to now; flag names confused with the trigger daemon's naming ('trigger.last-epoch' / 'trigger.this-epoch' in the queued task).","solutions":["Add the missing companion flag so both --last and --next are present.","Compute the two epochs from the subscription's billing period boundaries before invoking.","Prefer --auto-range when you just want the scheduled period and do not need custom bounds."],"exampleFix":"# before\n$ ./bin/phortune invoice --subscription PHID-PSUB-1234abcd --last 1690000000\n\n# after\n$ ./bin/phortune invoice --subscription PHID-PSUB-1234abcd --last 1690000000 --next 1692600000","handlingStrategy":"validation","validationCode":"// Both halves of the interval must be present.\nif (!$auto_range && ($last_epoch xor $next_epoch)) {\n  throw new InvalidArgumentException(\n    'Billing range is a closed interval: provide both --last and --next (or use --auto-range).');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate --last/--next from one source (the subscription's billing period boundaries) so they are always emitted as a pair.","Treat epochs as raw Unix timestamps; the workflow parses them via parseTimeArgument().","The queued worker stores them as trigger.last-epoch / trigger.this-epoch — keep naming straight to avoid half-filled pairs."],"tags":["php","phabricator","phortune","cli","usage-error","billing"],"backgroundTag":"missing-cli-argument","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}