phacility/phabricator · error · Exception

URI "%s" is not a valid fetchable resource. A valid fetchabl

Error message

URI "%s" is not a valid fetchable resource. A valid fetchable resource URI must specify a domain.

What it means

Third gate of requireValidRemoteURIForFetch(): the URI must include a non-empty domain (PhutilURI->getDomain()). Schemes with no host component (e.g. 'file:///etc/passwd', 'mailto:x@y' passed to a fetch context, or 'https:///path') pass protocol checks but give nothing to resolve, and host-less fetches are exactly the shape of filesystem/SSRF abuse, so they are rejected outright.

Source

Thrown at src/infrastructure/env/PhabricatorEnv.php:816

        pht(
          'URI "%s" is not a valid fetchable resource. A valid fetchable '.
          'resource URI must specify a protocol.',
          $raw_uri));
    }

    $protocols = array_fuse($protocols);
    if (!isset($protocols[$proto])) {
      throw new Exception(
        pht(
          'URI "%s" is not a valid fetchable resource. A valid fetchable '.
          'resource URI must use one of these protocols: %s.',
          $raw_uri,
          implode(', ', array_keys($protocols))));
    }

    $domain = $uri->getDomain();
    if (!strlen($domain)) {
      throw new Exception(
        pht(
          'URI "%s" is not a valid fetchable resource. A valid fetchable '.
          'resource URI must specify a domain.',
          $raw_uri));
    }

    $addresses = gethostbynamel($domain);
    if (!$addresses) {
      throw new Exception(
        pht(
          'URI "%s" is not a valid fetchable resource. The domain "%s" could '.
          'not be resolved.',
          $raw_uri,
          $domain));
    }

    foreach ($addresses as $address) {
      if (self::isBlacklistedOutboundAddress($address)) {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Supply a fully-qualified remote URI: 'https://example.com/resource'.
  2. If you meant a local file, this API is the wrong tool - local assets must be handled by dedicated import mechanisms, not remote fetch.
  3. Reject host-less URIs at the form/import boundary so users never store them.

Example fix

// before
PhabricatorEnv::requireValidRemoteURIForFetch('https:///img/logo.png', array('https'));
// throws: no domain

// after
PhabricatorEnv::requireValidRemoteURIForFetch('https://cdn.example.com/img/logo.png', array('https'));
Defensive patterns

Strategy: validation

Validate before calling

$uri = new PhutilURI($url);
if (!strlen($uri->getDomain())) {
  throw new Exception('Refusing to fetch host-less URI: '.$url);
}

Type guard

function uriHasHost($raw) {
  $uri = new PhutilURI($raw);
  return strlen($uri->getProtocol()) && strlen($uri->getDomain());
}

Try / catch

try {
  PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));
} catch (Exception $ex) {
  // host-less URIs are never fetchable; degrade to a placeholder resource
  return $default_image;
}

Prevention

When it happens

Trigger: requireValidRemoteURIForFetch() receiving 'file:///srv/secret' (file is not in allowed protocols normally, but any allowed scheme with empty host hits this branch), 'https:///resource', or a URI where the host was lost in templating.

Common situations: Attempts (accidental or hostile) to make Phabricator fetch local file:// resources; URL-building bugs that drop the host; imported data with truncated URLs; penetration tests probing the fetch validator.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/bbeba78b7346a698. Report an issue: GitHub.