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
- Check any custom aiohttp Resolver in use — it must return IP literals (not hostnames) in sockaddr; fix it to resolve before returning addr_info.
- Inspect the chained cause (OutboundPolicyError.__cause__) to see which address string failed parsing.
- 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).
- Ensure the system resolver (getaddrinfo/c-ares) is healthy; test with socket.getaddrinfo(host, port) directly.
- 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
- Do not stub aiohttp's resolver with hostname-bearing sockaddr values; always resolve to IP literals.
- Validate custom Resolver output in unit tests with ipaddress.ip_address().
- Inspect exc.__cause__ (the original ValueError) to identify the malformed address string.
- Keep the system resolver and c-ares builds standard unless you control addr_info formatting.
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
- MODEL_URL_CHECK_FAILED
- Resolved remote resource address is invalid
- HTTPClientError
- Remote resource returned HTTP
- Remote resource address is unsafe
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 excView on GitHub (pinned to 5e758547a8)