{"record":{"id":"b9b06f3395929dcb","repo":"phacility/phabricator","slug":"relative-ttl-must-be-zero-or-more-seconds-but-s","errorCode":null,"errorMessage":"Relative TTL must be zero or more seconds, but \"%s\" is negative.","messagePattern":"Relative TTL must be zero or more seconds, but \"(.+?)\" is negative\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/files/storage/PhabricatorFile.php","lineNumber":1496,"sourceCode":"    $absolute_ttl = idx($params, 'ttl.absolute');\n    $relative_ttl = idx($params, 'ttl.relative');\n    if ($absolute_ttl !== null && $relative_ttl !== null) {\n      throw new Exception(\n        pht(\n          'Specify an absolute TTL or a relative TTL, but not both.'));\n    } else if ($absolute_ttl !== null) {\n      if ($absolute_ttl < PhabricatorTime::getNow()) {\n        throw new Exception(\n          pht(\n            'Absolute TTL must be in the present or future, but TTL \"%s\" '.\n            'is in the past.',\n            $absolute_ttl));\n      }\n\n      $this->setTtl($absolute_ttl);\n    } else if ($relative_ttl !== null) {\n      if ($relative_ttl < 0) {\n        throw new Exception(\n          pht(\n            'Relative TTL must be zero or more seconds, but \"%s\" is '.\n            'negative.',\n            $relative_ttl));\n      }\n\n      $max_relative = phutil_units('365 days in seconds');\n      if ($relative_ttl > $max_relative) {\n        throw new Exception(\n          pht(\n            'Relative TTL must not be more than \"%s\" seconds, but TTL '.\n            '\"%s\" was specified.',\n            $max_relative,\n            $relative_ttl));\n      }\n\n      $absolute_ttl = PhabricatorTime::getNow() + $relative_ttl;\n","sourceCodeStart":1478,"sourceCodeEnd":1514,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/files/storage/PhabricatorFile.php#L1478-L1514","documentation":"A relative TTL was supplied as 'ttl.relative' (seconds from now), but the value is negative. Phabricator computes ttl = PhabricatorTime::getNow() + relative, and a negative result would create an already-expired file, which the absolute-TTL path also forbids. Only zero or positive durations are accepted.","triggerScenarios":"PhabricatorFile::newFromParams() with 'ttl.relative' => -3600 (or any int < 0). Typically the sign survives from a 'expires_in' vs 'expired_ago' mix-up, or from subtracting instead of adding when building the duration.","commonSituations":"Code that computes $ttl = $deadline - time() for a deadline that has already passed, yielding a negative remainder; swapping the operands of a subtraction ($now - $expiry instead of $expiry - $now); configuration tables storing -1 as 'no limit' that gets passed through as a TTL.","solutions":["Pass a non-negative number of seconds, e.g. 'ttl.relative' => max(0, $deadline - PhabricatorTime::getNow()).","If the intent is 'no expiry', omit the ttl keys entirely instead of sending a sentinel like -1.","Fix the operand order where the duration is computed; add an assertion that the deadline is in the future before computing the delta."],"exampleFix":"// before\n$params = array(\n  'ttl.relative' => $deadline - time(), // negative once deadline passed\n);\n\n// after\n$seconds_left = $deadline - PhabricatorTime::getNow();\n$params = array();\nif ($seconds_left > 0) {\n  $params['ttl.relative'] = $seconds_left;\n}","handlingStrategy":"validation","validationCode":"if (isset($params['ttl.relative'])) {\n  $params['ttl.relative'] = max(0, (int)$params['ttl.relative']);\n}","typeGuard":"function isNonNegativeSeconds($value): bool {\n  return is_int($value) && $value >= 0;\n}","tryCatchPattern":null,"preventionTips":["Compute deltas as $deadline - now (not now - $deadline) and assert the deadline is in the future first.","Never use -1 or other sentinels as 'no TTL' — omit the key instead.","Clamp user-supplied retention values with max(0, ...) at the input boundary."],"tags":["phabricator","file-storage","ttl","duration","php"],"backgroundTag":"negative-duration-value","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}