phacility/phabricator · error · Exception
Specify an absolute TTL or a relative TTL, but not both.
Error message
Specify an absolute TTL or a relative TTL, but not both.
What it means
PhabricatorFile rejects file-creation parameters that set the file expiry twice: 'ttl.absolute' (a Unix epoch timestamp) and 'ttl.relative' (seconds from now) are two mutually exclusive ways to set a TTL. If both keys hold non-null values, the parameter validation in PhabricatorFile::buildFileFromParams() throws before any storage engine is touched. Only one TTL style may be supplied per file.
Source
Thrown at src/applications/files/storage/PhabricatorFile.php:1481
'canCDN' => 'optional bool',
'profile' => 'optional bool',
'format' => 'optional string|PhabricatorFileStorageFormat',
'mime-type' => 'optional string',
'builtin' => 'optional string',
'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.',View on GitHub (pinned to 5720a38cfe)
Solutions
- Delete one of the two keys from the params array; keep only the TTL style the caller actually wants.
- If you merge defaults into user input, only inject the default relative TTL when 'ttl.absolute' is absent, and vice versa (unset the unused key).
- In a UI or API client that exposes both options, make them mutually exclusive inputs (radio buttons, or drop one field) before the request reaches the server.
Example fix
// before $params = array( 'ttl.absolute' => $expires_epoch, 'ttl.relative' => 86400, ); $file = PhabricatorFile::newFromParams($params); // after $params = array( 'ttl.absolute' => $expires_epoch, ); $file = PhabricatorFile::newFromParams($params);
Defensive patterns
Strategy: validation
Validate before calling
$has_absolute = (idx($params, 'ttl.absolute') !== null);
$has_relative = (idx($params, 'ttl.relative') !== null);
if ($has_absolute && $has_relative) {
unset($params['ttl.relative']); // pick one policy explicitly
}
$file = PhabricatorFile::newFromParams($params); Type guard
function paramsHaveSingleTtl(array $params): bool {
$set = 0;
if (idx($params, 'ttl.absolute') !== null) { $set++; }
if (idx($params, 'ttl.relative') !== null) { $set++; }
return $set <= 1;
} Try / catch
try {
$file = PhabricatorFile::newFromParams($params);
} catch (Exception $ex) {
if (preg_match('/not both/', $ex->getMessage())) {
unset($params['ttl.relative']);
$file = PhabricatorFile::newFromParams($params);
} else {
throw $ex;
}
} Prevention
- Build the params array in one place with an explicit if/else for TTL style instead of merging arrays.
- In API clients, make absolute and relative TTL mutually exclusive fields at the schema level.
- Add a unit test asserting exactly one ttl key is present in every params array a helper produces.
When it happens
Trigger: Calling PhabricatorFile::newFromParams() (or anything that funnels into buildFileFromParams: the file.upload Conduit method, PhabricatorFileUploadSource, PhabricatorFileQuery writes) with a $params array where both $params['ttl.absolute'] and $params['ttl.relative'] are set. This exact branch runs when idx($params, 'ttl.absolute') !== null AND idx($params, 'ttl.relative') !== null.
Common situations: An upload wrapper that injects a default relative TTL (e.g. ttl.relative => 86400) on top of a user-supplied absolute expiry; a refactor from absolute to relative TTL that left the old key in the params array; a Conduit client that copies every field from a form into the request and sends both ttl inputs.
Related errors
- Absolute TTL must be in the present or future, but TTL "%s"
- 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/6ad2800dea379623.
Report an issue: GitHub.