{"record":{"id":"5acf74d802b30cfb","repo":"phacility/phabricator","slug":"uri-s-is-not-a-valid-fetchable-resource-a-vali","errorCode":null,"errorMessage":"URI \"%s\" is not a valid fetchable resource. A valid fetchable resource URI must specify a protocol.","messagePattern":"URI \"(.+?)\" is not a valid fetchable resource\\. A valid fetchable resource URI must specify a protocol\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/infrastructure/env/PhabricatorEnv.php","lineNumber":797,"sourceCode":"   *\n   * A valid fetchable remote resource can be safely fetched using a request\n   * originating on this server. This is a primarily an address check against\n   * the outbound address blacklist.\n   *\n   * @param string URI to test.\n   * @param list<string> Allowed protocols.\n   * @return pair<string, string> Pre-resolved URI and domain.\n   * @task uri\n   */\n  public static function requireValidRemoteURIForFetch(\n    $raw_uri,\n    array $protocols) {\n\n    $uri = new PhutilURI($raw_uri);\n\n    $proto = $uri->getProtocol();\n    if (!strlen($proto)) {\n      throw new Exception(\n        pht(\n          'URI \"%s\" is not a valid fetchable resource. A valid fetchable '.\n          'resource URI must specify a protocol.',\n          $raw_uri));\n    }\n\n    $protocols = array_fuse($protocols);\n    if (!isset($protocols[$proto])) {\n      throw new Exception(\n        pht(\n          'URI \"%s\" is not a valid fetchable resource. A valid fetchable '.\n          'resource URI must use one of these protocols: %s.',\n          $raw_uri,\n          implode(', ', array_keys($protocols))));\n    }\n\n    $domain = $uri->getDomain();\n    if (!strlen($domain)) {","sourceCodeStart":779,"sourceCodeEnd":815,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/env/PhabricatorEnv.php#L779-L815","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Normalize the URI before validating: if no scheme, prepend 'https://' (or reject with a user-facing error).","Pass protocols explicitly per call site (e.g. array('http', 'https') for image fetches) so the whitelist matches what your HTTP client actually supports.","Test with PhabricatorEnv::isValidRemoteURIForFetch() (non-throwing twin) to branch gracefully instead of catching."],"exampleFix":"// before\nPhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));\n// $url = 'example.com/a.png' -> throws: no protocol\n\n// after\nif (!preg_match('/^[a-z][a-z0-9+\\.-]*:/i', $url)) {\n  $url = 'https://'.$url;\n}\nPhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));","handlingStrategy":"validation","validationCode":"$uri = new PhutilURI($raw);\nif (!strlen($uri->getProtocol())) {\n  if (!preg_match('/^[a-z][a-z0-9+\\.-]*:/i', $raw)) {\n    $raw = 'https://'.$raw; // absolutize bare hosts\n  }\n}\nPhabricatorEnv::requireValidRemoteURIForFetch($raw, array('http', 'https'));","typeGuard":"function isFetchableURI($raw, array $protocols) {\n  $uri = new PhutilURI($raw);\n  return strlen($uri->getProtocol()) && strlen($uri->getDomain());\n}","tryCatchPattern":"try {\n  PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));\n} catch (Exception $ex) {\n  // show the user a form error instead of an uncaught 500 during fetch\n  $e_url = pht('Not a valid fetchable URI: %s', $ex->getMessage());\n}","preventionTips":["Run PhabricatorEnv::isValidRemoteURIForFetch() before initiating any user-driven fetch.","Always pass an explicit protocol list matched to your HTTP client's capabilities.","Absolutize scheme-less input at the boundary; never let raw user strings reach the fetcher."],"tags":["phabricator","uri","validation","fetch","ssrf","security"],"backgroundTag":"uri-missing-protocol","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}