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 protocol.

What it means

PhabricatorEnv::requireValidRemoteURIForFetch() is the stricter sibling of the link validator, used when Phabricator itself will retrieve the URI (repository imports, image fetching, MFA SMS/voice lookups, webhooks). The caller supplies the allowed protocol list; first the URI must carry a non-empty protocol from PhutilURI. A protocol-less target cannot be fetched safely, so it throws before any network activity.

Source

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

   *
   * A valid fetchable remote resource can be safely fetched using a request
   * originating on this server. This is a primarily an address check against
   * the outbound address blacklist.
   *
   * @param string URI to test.
   * @param list<string> Allowed protocols.
   * @return pair<string, string> Pre-resolved URI and domain.
   * @task uri
   */
  public static function requireValidRemoteURIForFetch(
    $raw_uri,
    array $protocols) {

    $uri = new PhutilURI($raw_uri);

    $proto = $uri->getProtocol();
    if (!strlen($proto)) {
      throw new Exception(
        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)) {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Normalize the URI before validating: if no scheme, prepend 'https://' (or reject with a user-facing error).
  2. Pass protocols explicitly per call site (e.g. array('http', 'https') for image fetches) so the whitelist matches what your HTTP client actually supports.
  3. Test with PhabricatorEnv::isValidRemoteURIForFetch() (non-throwing twin) to branch gracefully instead of catching.

Example fix

// before
PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));
// $url = 'example.com/a.png' -> throws: no protocol

// after
if (!preg_match('/^[a-z][a-z0-9+\.-]*:/i', $url)) {
  $url = 'https://'.$url;
}
PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));
Defensive patterns

Strategy: validation

Validate before calling

$uri = new PhutilURI($raw);
if (!strlen($uri->getProtocol())) {
  if (!preg_match('/^[a-z][a-z0-9+\.-]*:/i', $raw)) {
    $raw = 'https://'.$raw; // absolutize bare hosts
  }
}
PhabricatorEnv::requireValidRemoteURIForFetch($raw, array('http', 'https'));

Type guard

function isFetchableURI($raw, array $protocols) {
  $uri = new PhutilURI($raw);
  return strlen($uri->getProtocol()) && strlen($uri->getDomain());
}

Try / catch

try {
  PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));
} catch (Exception $ex) {
  // show the user a form error instead of an uncaught 500 during fetch
  $e_url = pht('Not a valid fetchable URI: %s', $ex->getMessage());
}

Prevention

When it happens

Trigger: Calling requireValidRemoteURIForFetch($uri, array('http','https')) with $uri like 'example.com/file.png' or '/local/path' - PhutilURI->getProtocol() returns '' and the exception fires before DNS or HTTP layers run.

Common situations: User-supplied image/avatar/import URLs pasted without a scheme; stored rows from an older schema that hold relative URLs; glue code forwarding a 'url' request parameter directly into a fetch call.

Related errors


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