{"record":{"id":"5063e5d302f3394c","repo":"phacility/phabricator","slug":"absolute-ttl-must-be-in-the-present-or-future-but","errorCode":null,"errorMessage":"Absolute TTL must be in the present or future, but TTL \"%s\" is in the past.","messagePattern":"Absolute TTL must be in the present or future, but TTL \"(.+?)\" is in the past\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/files/storage/PhabricatorFile.php","lineNumber":1486,"sourceCode":"        'storageEngines' => 'optional list<PhabricatorFileStorageEngine>',\n        'chunk' => 'optional bool',\n      ));\n\n    $file_name = idx($params, 'name');\n    $this->setName($file_name);\n\n    $author_phid = idx($params, 'authorPHID');\n    $this->setAuthorPHID($author_phid);\n\n    $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) {","sourceCodeStart":1468,"sourceCodeEnd":1504,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/files/storage/PhabricatorFile.php#L1468-L1504","documentation":"An absolute TTL was supplied as 'ttl.absolute', but the epoch timestamp is earlier than PhabricatorTime::getNow() (the server's current time). Phabricator refuses to create a file that would already be expired at creation time, because the TTL drives automatic deletion of expired files. The comparison is strict: a TTL exactly equal to now passes, anything earlier throws.","triggerScenarios":"PhabricatorFile::newFromParams() with 'ttl.absolute' set to a Unix timestamp in seconds that is less than the server's current time — e.g. time() - 60, a cached expires_at computed on a previous request, or a value derived from a row where ttl was already stored.","commonSituations":"Server clock skew: the web/daemon host generating the timestamp lags the Phabricator host (or PhabricatorTime is offset for tests), so a 'just now' timestamp reads as past; unit confusion (sending milliseconds instead of seconds gives a huge past-looking number only if truncated; sending seconds-from-epoch of 0 or a formatted date string instead of an epoch); retrying an old failed request with its original expiry.","solutions":["Send a timestamp strictly in the future, e.g. PhabricatorTime::getNow() + $seconds, or just use 'ttl.relative' => $seconds which does that arithmetic for you.","If you compute expires_at earlier in a long-running job, recompute it immediately before calling newFromParams().","Check clock sync (NTP) between the machine building the timestamp and the Phabricator server; verify the value is epoch seconds, not milliseconds or a date string."],"exampleFix":"// before\n$params = array(\n  'ttl.absolute' => $this->expiresAt, // computed minutes ago, may now be past\n);\n\n// after\n$params = array(\n  'ttl.relative' => $ttl_seconds, // server computes now + seconds\n);","handlingStrategy":"validation","validationCode":"if ($ttl !== null && $ttl < PhabricatorTime::getNow()) {\n  $ttl = null; // or recompute: $ttl = PhabricatorTime::getNow() + $default_seconds;\n}","typeGuard":"function isFutureTimestamp($ttl): bool {\n  return is_int($ttl) && $ttl >= PhabricatorTime::getNow();\n}","tryCatchPattern":"try {\n  $file = PhabricatorFile::newFromParams($params);\n} catch (Exception $ex) {\n  if (preg_match('/is in the past/', $ex->getMessage())) {\n    unset($params['ttl.absolute']);\n    $file = PhabricatorFile::newFromParams($params);\n  } else {\n    throw $ex;\n  }\n}","preventionTips":["Compute absolute TTLs as PhabricatorTime::getNow() + offset at the last moment before the call.","Prefer 'ttl.relative' whenever the intent is 'expires N seconds from creation'.","Keep server clocks NTP-synced and verify timestamps are epoch seconds, not milliseconds or date strings."],"tags":["phabricator","file-storage","ttl","timestamp","php"],"backgroundTag":"timestamp-in-past","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}