iflytek/astron-agent · error · HTTPClientException

HTTPClientError

HTTPClientError

Error message

{exc}

What it means

fetch_public_resource is the SSRF-hardened remote-file downloader. When any of its internal policy checks (RemoteResourcePolicyError) reject the URL, address, redirect, or payload, the policy reason is surfaced as an HTTPClientException with code HTTPClientError and the reason appended via extra_message. This means the download was refused by the library's safety validation, not that the network failed.

Solutions

  1. Read extra_message (str(exc) from the wrapped RemoteResourcePolicyError) — it states the exact policy reason; fix the URL accordingly.
  2. Ensure the URL is a well-formed http(s) URL with hostname, no credentials, no fragment, and points at a public address.
  3. If the resource legitimately exceeds the limit, pass a larger max_bytes explicitly.
  4. For private object storage, use a URL under the configured OSS_DOWNLOAD_HOST origin/bucket so _is_configured_storage_url authorizes it (requires OSS_TYPE=s3).

Example fix

// before
await fetch_public_resource("ftp://files.example.com/report.pdf")
// after
await fetch_public_resource("https://files.example.com/report.pdf")
Defensive patterns

Strategy: try-catch

Validate before calling

from urllib.parse import urlsplit
p = urlsplit(url)
assert url.startswith(("http://", "https://")) and p.hostname and "@" not in p.netloc and not p.fragment

Type guard

def is_safe_url(u): 
    from urllib.parse import urlsplit
    try:
        p = urlsplit(u)
    except ValueError:
        return False
    return isinstance(u, str) and p.scheme in ("http", "https") and bool(p.hostname) and "@" not in p.netloc

Try / catch

try:
    data = await fetch_public_resource(url)
except HTTPClientException as e:
    log.warning("download rejected: %s", e)
    return None

Prevention

When it happens

Trigger: Any RemoteResourcePolicyError raised inside fetch_public_resource: malformed URL, disallowed scheme, private/reserved target IP, user-info in URL, non-2xx status, oversized body, or invalid size limit (max_bytes <= 0). Callers include gen_params and req_ase_ability_ocr_service.

Common situations: Passing a file:// or ftp:// URL; pointing at localhost or an internal 10.x/192.168.x address; a URL containing user:pass@; a signed URL with a #fragment; a storage host that redirects (302) to a CDN; a file larger than the 50MB default limit.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

        parsed, _ = _validate_resource_url(url)
        hostname = _normalize_hostname(parsed.hostname or "")
        connector = aiohttp.TCPConnector(
            use_dns_cache=False,
            socket_factory=create_public_socket_factory(url),
        )
        timeout = aiohttp.ClientTimeout(
            total=_positive_float_setting(AIOHTTP_CLIENT_TOTAL_TIMEOUT_KEY, 300.0),
            connect=_positive_float_setting(AIOHTTP_CLIENT_CONNECT_TIMEOUT_KEY, 10.0),
            sock_read=_positive_float_setting(AIOHTTP_CLIENT_READ_TIMEOUT_KEY, 60.0),
        )
        return await _download_resource(url, connector, timeout, max_bytes)
    except RemoteResourcePolicyError as exc:
        log.warning(
            "Remote resource download rejected, host={}, reason={}", hostname, exc
        )
        if span is not None:
            span.add_error_event("Remote resource download rejected")
        raise HTTPClientException.from_error_code(
            CodeEnums.HTTPClientError,
            extra_message=str(exc),
        ) from exc
    except Exception as exc:
        log.debug(
            "Remote resource download failed, host={}, error_type={}",
            hostname,
            type(exc).__name__,
        )
        if span is not None:
            span.add_error_event("Remote resource download failed")
        raise HTTPClientException.from_error_code(
            CodeEnums.HTTPClientError,
            extra_message="Remote resource download failed",
        ) from exc


async def _download_resource(

View on GitHub (pinned to 5e758547a8)