iflytek/astron-agent · error · OutboundPolicyError

Resolved outbound address is invalid

Error message

Resolved outbound address is invalid

What it means

OutboundPolicyError raised inside socket_factory when ipaddress.ip_address() cannot parse the sockaddr returned by DNS resolution for the aiohttp connection. This indicates the resolver produced an address string that is not a valid IP literal — essentially never expected from a healthy stack, so it is treated as a hard policy failure rather than retried. The chained ValueError from ip_address is preserved as __cause__.

Solutions

  1. Check any custom aiohttp Resolver in use — it must return IP literals (not hostnames) in sockaddr; fix it to resolve before returning addr_info.
  2. Inspect the chained cause (OutboundPolicyError.__cause__) to see which address string failed parsing.
  3. If tests stub addr_info, construct it with real ipaddress-parseable strings, e.g. ('93.184.216.34', 80) or ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0).
  4. Ensure the system resolver (getaddrinfo/c-ares) is healthy; test with socket.getaddrinfo(host, port) directly.
  5. Strip IPv6 scope IDs (%eth0) at the resolver level or use a non-link-local address.

Example fix

# before (custom test stub)
addr_info = (socket.AF_INET, socket.SOCK_STREAM, 6, '', ('example.com', 80))
# after
addr_info = (socket.AF_INET, socket.SOCK_STREAM, 6, '', ('93.184.216.34', 80))
Defensive patterns

Strategy: try-catch

Validate before calling

import socket, ipaddress

def resolver_returns_ips(host: str) -> bool:
    try:
        infos = socket.getaddrinfo(host, None)
    except socket.gaierror:
        return False
    return all(_parseable(info[4][0]) for info in infos)

def _parseable(value: str) -> bool:
    try:
        ipaddress.ip_address(value)
        return True
    except ValueError:
        return False

Type guard

def is_valid_addr_info(addr_info) -> bool:
    try:
        sockaddr = addr_info[4]
        ipaddress.ip_address(sockaddr[0])
        return True
    except (IndexError, ValueError, TypeError):
        return False

Try / catch

try:
    sock = socket_factory(addr_info)
except OutboundPolicyError as exc:
    logger.error("bad resolved addr_info %r: cause=%r", addr_info, exc.__cause__)
    raise

Prevention

When it happens

Trigger: create_socket_factory's returned socket_factory receiving an aiohttp.AddrInfoType whose sockaddr[0] is not a parseable IP string — e.g. a custom resolver or patched event loop returning hostnames/empty strings in addr_info, or an unusual AI_* family entry the address parser cannot handle.

Common situations: Test environments stubbing aiohttp's resolver with malformed sockaddr values; custom Resolver implementations returning sockaddr with a hostname instead of an IP; corrupted/crafted addrinfo from a custom c-ares build; IPv6 scope-id strings ('fe80::1%eth0') that ipaddress rejects in some contexts.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at core/plugin/link/infra/tool_exector/ssrf_guard.py:155

        raise OutboundPolicyError("Tool path must not change the endpoint origin")


def create_socket_factory(
    policy: OutboundPolicy,
    target_url: str,
) -> Callable[[aiohttp.AddrInfoType], socket.socket]:
    """Create an aiohttp socket factory that checks the actual target sockaddr."""
    parsed = policy.validate_url(target_url)
    hostname = _normalize_hostname(parsed.hostname or "")
    literal_host = _parse_ip(hostname) is not None
    allow_private_endpoint = policy.is_private_endpoint_allowed(parsed)

    def socket_factory(addr_info: aiohttp.AddrInfoType) -> socket.socket:
        family, type_, proto, _, sockaddr = addr_info
        try:
            address = ipaddress.ip_address(sockaddr[0])
        except ValueError as exc:
            raise OutboundPolicyError("Resolved outbound address is invalid") from exc
        policy.validate_address(
            address,
            allow_private_endpoint=allow_private_endpoint,
            allow_literal_exception=literal_host,
        )
        return socket.socket(family=family, type=type_, proto=proto)

    return socket_factory


def _origin(url: str) -> Tuple[str, str, int]:
    try:
        parsed = urlsplit(url)
        scheme = parsed.scheme.lower()
        hostname = _normalize_hostname(parsed.hostname or "")
        port = parsed.port
    except (TypeError, ValueError) as exc:
        raise OutboundPolicyError("Outbound URL is malformed") from exc

View on GitHub (pinned to 5e758547a8)