iflytek/astron-agent · error · RemoteResourcePolicyError

Remote resource URL must include a hostname

Error message

Remote resource URL must include a hostname

What it means

_validate_parsed_resource_url requires a non-empty hostname. URLs whose netloc is empty or consist only of a scheme+path (e.g. 'https:///path' or 'https://file.pdf') are rejected because there is no remote host to validate against, making SSRF/destination checks impossible.

Solutions

  1. Include the full authority in the URL: 'https://host.example.com/path'.
  2. If building from parts, use urllib.parse.urljoin(base, path) with a complete absolute base URL.
  3. Check the config/env value supplying the base URL contains the hostname.

Example fix

// before
url = "/bucket/file.pdf"  # no host
await fetch_public_resource(url)
// after
from urllib.parse import urljoin
url = urljoin("https://files.example.com", "/bucket/file.pdf")
await fetch_public_resource(url)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlsplit
assert bool(urlsplit(url).hostname), "URL must include a hostname"

Type guard

def has_hostname(u):
    try:
        return bool(urlsplit(u).hostname)
    except (TypeError, ValueError):
        return False

Try / catch

try:
    data = await fetch_public_resource(url)
except HTTPClientException as e:
    if "hostname" in str(e):
        ...  # fix URL construction / base-url config

Prevention

When it happens

Trigger: URL like 'https:///file' or 'http://:8080/x'; string slicing bugs that drop the host; passing a path-only string after accidentally splitting off the domain; 'https://?query=1'.

Common situations: Template concatenation bugs (f'{base}{path}' where base is empty); joining URL parts with a helper that lost the host; config value for a base URL missing the domain (e.g. OSS download host configured as just '/bucket').

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/814ba3073342b34b. Report an issue: GitHub.

Appendix: source

Thrown at core/plugin/aitools/common/clients/safe_download.py:213


def _validate_url_characters(url: str) -> None:
    if not isinstance(url, str) or any(
        ord(character) < 0x20 or ord(character) == 0x7F for character in url
    ):
        raise RemoteResourcePolicyError("Remote resource URL is malformed")


def _validate_parsed_resource_url(
    parsed: SplitResult,
    port: Optional[int],
) -> None:
    if parsed.scheme.lower() not in _ALLOWED_SCHEMES:
        raise RemoteResourcePolicyError(
            "Only HTTP and HTTPS remote resources are allowed"
        )
    if not parsed.hostname:
        raise RemoteResourcePolicyError("Remote resource URL must include a hostname")
    if parsed.username is not None or parsed.password is not None:
        raise RemoteResourcePolicyError(
            "Remote resource URL must not include user information"
        )
    if "\\" in parsed.netloc:
        raise RemoteResourcePolicyError("Remote resource URL authority is invalid")
    if parsed.fragment:
        raise RemoteResourcePolicyError(
            "Remote resource URL must not include a fragment"
        )
    if port is not None and not 1 <= port <= 65535:
        raise RemoteResourcePolicyError("Remote resource URL port is invalid")


def _normalize_hostname(hostname: str) -> str:
    value = hostname.strip().lower().rstrip(".")
    if _parse_ip(value) is not None:
        return value

View on GitHub (pinned to 5e758547a8)