phacility/phabricator · error · Exception
Absolute TTL must be in the present or future, but TTL "%s"
Error message
Absolute TTL must be in the present or future, but TTL "%s" is in the past.
What it means
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.
Source
Thrown at src/applications/files/storage/PhabricatorFile.php:1486
'storageEngines' => 'optional list<PhabricatorFileStorageEngine>',
'chunk' => 'optional bool',
));
$file_name = idx($params, 'name');
$this->setName($file_name);
$author_phid = idx($params, 'authorPHID');
$this->setAuthorPHID($author_phid);
$absolute_ttl = idx($params, 'ttl.absolute');
$relative_ttl = idx($params, 'ttl.relative');
if ($absolute_ttl !== null && $relative_ttl !== null) {
throw new Exception(
pht(
'Specify an absolute TTL or a relative TTL, but not both.'));
} else if ($absolute_ttl !== null) {
if ($absolute_ttl < PhabricatorTime::getNow()) {
throw new Exception(
pht(
'Absolute TTL must be in the present or future, but TTL "%s" '.
'is in the past.',
$absolute_ttl));
}
$this->setTtl($absolute_ttl);
} else if ($relative_ttl !== null) {
if ($relative_ttl < 0) {
throw new Exception(
pht(
'Relative TTL must be zero or more seconds, but "%s" is '.
'negative.',
$relative_ttl));
}
$max_relative = phutil_units('365 days in seconds');
if ($relative_ttl > $max_relative) {View on GitHub (pinned to 5720a38cfe)
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.
Example fix
// before $params = array( 'ttl.absolute' => $this->expiresAt, // computed minutes ago, may now be past ); // after $params = array( 'ttl.relative' => $ttl_seconds, // server computes now + seconds );
Defensive patterns
Strategy: validation
Validate before calling
if ($ttl !== null && $ttl < PhabricatorTime::getNow()) {
$ttl = null; // or recompute: $ttl = PhabricatorTime::getNow() + $default_seconds;
} Type guard
function isFutureTimestamp($ttl): bool {
return is_int($ttl) && $ttl >= PhabricatorTime::getNow();
} Try / catch
try {
$file = PhabricatorFile::newFromParams($params);
} catch (Exception $ex) {
if (preg_match('/is in the past/', $ex->getMessage())) {
unset($params['ttl.absolute']);
$file = PhabricatorFile::newFromParams($params);
} else {
throw $ex;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Specify an absolute TTL or a relative TTL, but not both.
- Relative TTL must be zero or more seconds, but "%s" is negat
- Relative TTL must not be more than "%s" seconds, but TTL "%s
- Unable to upload file: this server is not configured with an
- Service "%s" is unrecognized, restricted, or you do not have
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/5063e5d302f3394c.
Report an issue: GitHub.