phacility/phabricator · error · Exception
Only static resources may be served.
Error message
Only static resources may be served.
What it means
CelerityResourceController serves files from disk based on a request path, so it locks down what it will read: after rejecting path traversal ('//' or '..'), it maps the file extension to a type via getResourceType() and throws unless that type is in getSupportedResourceTypes() (css, js, png, svg, gif, jpg, swf, woff, woff2, eot, ...). This keeps the controller from becoming an arbitrary-file reader.
Source
Thrown at src/applications/celerity/controller/CelerityResourceController.php:41
}
abstract public function getCelerityResourceMap();
protected function serveResource(array $spec) {
$path = $spec['path'];
$hash = idx($spec, 'hash');
// Sanity checking to keep this from exposing anything sensitive, since it
// ultimately boils down to disk reads.
if (preg_match('@(//|\.\.)@', $path)) {
return new Aphront400Response();
}
$type = CelerityResourceTransformer::getResourceType($path);
$type_map = self::getSupportedResourceTypes();
if (empty($type_map[$type])) {
throw new Exception(pht('Only static resources may be served.'));
}
$dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode');
$map = $this->getCelerityResourceMap();
$expect_hash = $map->getHashForName($path);
// Test if the URI hash is correct for our current resource map. If it
// is not, refuse to cache this resource. This avoids poisoning caches
// and CDNs if we're getting a request for a new resource to an old node
// shortly after a push.
$is_cacheable = ($hash === $expect_hash);
$is_locally_cacheable = $this->isLocallyCacheableResourceType($type);
if (AphrontRequest::getHTTPHeader('If-Modified-Since') && $is_cacheable) {
// Return a "304 Not Modified". We don't care about the value of this
// field since we never change what resource is served by a given URI.
return $this->makeResponseCacheable(new Aphront304Response());
}View on GitHub (pinned to 5720a38cfe)
Solutions
- Serve the file with a supported extension or from a different controller that is designed for that content
- If you maintain a fork and genuinely need a new type, extend getSupportedResourceTypes() with the extension => content-type entry
- For source-map noise, generate maps without deploying them, or publish them under a static host rather than /res/
Example fix
// before (fork serves /res/<hash>/js/app.js.map -> exception)
// after: register the type in the controller
public static function getSupportedResourceTypes() {
return array(
'css' => 'text/css; charset=utf-8',
'js' => 'text/javascript; charset=utf-8',
'map' => 'application/json; charset=utf-8',
// ...
);
} Defensive patterns
Strategy: validation
Validate before calling
$ext = strtolower(last(explode('.', $path)));
$supported = array_keys(CelerityResourceController::getSupportedResourceTypes());
if (!in_array($ext, $supported, true)) {
// Serve the file through a different, purpose-built controller instead.
return new Aphront404Response();
} Prevention
- Only reference celerity resources with extensions present in getSupportedResourceTypes()
- Do not point /res/ at generated artifacts like .map or .json — serve those elsewhere
- If maintaining a fork that adds a type, extend the type map and add a test covering the new extension
When it happens
Trigger: A /res/ request for a file whose extension is not in the supported map, e.g. '.map', '.txt', '.json', or any unknown suffix; Adding a new asset type to a fork (e.g. '.webmanifest') and requesting it without extending getSupportedResourceTypes(); Stale URLs requesting files that were replaced with a different extension
Common situations: Forks shipping new static file kinds (source maps, manifest files) through celerity; browsers auto-requesting '<x>.map' for JS served from /res/; renames like .js to .mjs.
Related errors
- No resource source exists with name "%s"!
- Attempting to resolve unknown resource, "%s".
- Request parameter "%s" is not formatted properly. Expected a
- Request parameter "%s" is not formatted properly. Expected a
- Invalid Request (CSRF)
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/2634dedac6f6846e.
Report an issue: GitHub.