phacility/phabricator · error · Exception

URI "%s" is not a valid linkable resource. A valid linkable

Error message

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

What it means

The final gate of requireValidRemoteURIForLink(): after protocol checks, PhutilURI->getDomain() must be non-empty. URIs like 'mailto:someone@example.com' variants without a host, 'https:///path', or scheme-only strings pass a protocol test but have no domain, and a link with no host cannot be considered a well-formed remote resource, so the plain Exception is thrown.

Source

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

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

    $protocols = self::getEnvConfig('uri.allowed-protocols');
    if (!isset($protocols[$proto])) {
      throw new Exception(
        pht(
          'URI "%s" is not a valid linkable resource. A valid linkable '.
          '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 linkable resource. A valid linkable '.
          'resource URI must specify a domain.',
          $raw_uri));
    }
  }


  /**
   * Detect if a URI identifies a valid fetchable remote resource.
   *
   * @param string URI to test.
   * @param list<string> Allowed protocols.
   * @return bool True if the URI is a valid fetchable remote resource.
   * @task uri
   */
  public static function isValidRemoteURIForFetch($uri, array $protocols) {
    try {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Fix the source value to include a host: 'https://example.com/path'.
  2. Add a form-level validation that rejects host-less URIs before they reach storage, so users get an inline error instead of an exception at render time.
  3. For scheme-only URIs (mailto:, tel:) check whether your allowed-protocols entries even make sense - mailto with a domain is nonsensical; consider excluding such schemes from link fields.

Example fix

// before
$uri = 'https:///repos/P1'; // no host
PhabricatorEnv::requireValidRemoteURIForLink($uri); // throws

// after
$uri = 'https://phab.example.com/repos/P1';
PhabricatorEnv::requireValidRemoteURIForLink($uri);
Defensive patterns

Strategy: validation

Validate before calling

$uri = new PhutilURI($url);
if (!strlen($uri->getDomain())) {
  throw new Exception('URI must include a host: '.$url);
}
PhabricatorEnv::requireValidRemoteURIForLink($url);

Type guard

function uriHasDomain($raw) {
  return strlen((new PhutilURI($raw))->getDomain()) > 0;
}

Try / catch

try {
  PhabricatorEnv::requireValidRemoteURIForLink($url);
} catch (Exception $ex) {
  // do not link host-less URIs; display them escaped
  return htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
}

Prevention

When it happens

Trigger: Calling requireValidRemoteURIForLink() with values such as 'https:///just/a/path', 'custom-scheme:' or 'mailto:' with empty remainder - protocol parses, but getDomain() returns '' and strlen() fails.

Common situations: User input that kept only a scheme after cleanup ('https://' alone); templating bugs dropping the host portion; imported records with truncated URLs that lost everything after '://'.

Related errors


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