iflytek/astron-agent · error · OutboundPolicyError
Outbound URL origin is invalid
Error message
Outbound URL origin is invalid
What it means
`_origin` rejects URLs whose scheme is not exactly `http` or `https`, or whose hostname normalizes to empty, raising `OutboundPolicyError('Outbound URL origin is invalid')`. The library only treats http/https origins as safe for outbound tool calls; anything else (ftp://, file://, ws://) or a URL without a usable host is refused before any network I/O.
Solutions
- Prefix the scheme explicitly: ensure the URL starts with `http://` or `https://` before passing it to the guard.
- Check that a hostname is present after `//` in the URL; a trailing path with no authority (`https:///path`) is rejected.
- If a non-HTTP protocol is genuinely required, this guard intentionally forbids it — route through an approved HTTP gateway instead of weakening the check.
- Validate with `urlsplit(url)` in your own code: assert `url.scheme in ('http','https')` and `url.hostname` is non-empty before invoking the tool.
Example fix
// before tool_url = "api.example.com/chat" # no scheme -> origin invalid // after tool_url = "https://api.example.com/chat"
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
def has_http_origin(url) -> bool:
if not isinstance(url, str):
return False
p = urlsplit(url)
return p.scheme.lower() in ("http", "https") and bool(p.hostname) Type guard
def is_http_url(value) -> bool:
if not isinstance(value, str):
return False
p = urlsplit(value)
return p.scheme in ("http", "https") and bool(p.hostname) Try / catch
try:
ensure_same_origin(base_url, candidate_url)
except OutboundPolicyError:
raise ValueError(f"Tool URL must be an absolute http(s) URL with a hostname: {candidate_url!r}") Prevention
- Store tool endpoints as full absolute URLs including scheme in config and env.
- Add a config-load-time assertion that every URL field starts with http:// or https://.
- Never accept relative or scheme-less URLs from user input; normalize with urljoin against a trusted base.
- Reject file://, ftp://, and custom schemes at the ingestion boundary.
When it happens
Trigger: `ensure_same_origin(base, candidate)` or `_endpoint(parsed)` receiving a URL like `ftp://host/path`, a scheme-less string (`example.com/api` with no `https://`), or `https:///path` (empty host). Also fires when `_normalize_hostname` returns an empty string because `parsed.hostname` was empty/whitespace.
Common situations: Config values written without the scheme (`TOOL_URL=api.example.com`); scheme accidentally stripped by a proxy/normalization step; relative URLs passed where absolute ones are required; `file://` or custom-scheme URLs smuggled into a tool definition.
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
- Only HTTP and HTTPS remote resources are allowed
- Outbound URL is malformed
- Outbound URL must not include user information
- Skill resource URL is not allowed
- MODEL_URL_CHECK_FAILED
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/363ba1c526f71aac.
Report an issue: GitHub.
Appendix: source
Thrown at core/plugin/link/infra/tool_exector/ssrf_guard.py:175
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
if scheme not in _ALLOWED_SCHEMES or not hostname:
raise OutboundPolicyError("Outbound URL origin is invalid")
if parsed.username is not None or parsed.password is not None:
raise OutboundPolicyError("Outbound URL must not include user information")
normalized_port = port if port is not None else (443 if scheme == "https" else 80)
return scheme, hostname, normalized_port
def _parse_http_url(url: str) -> SplitResult:
if not isinstance(url, str):
raise OutboundPolicyError("Outbound URL is malformed")
_validate_url_characters(url)
try:
parsed = urlsplit(url)
port = parsed.port
except (TypeError, ValueError) as exc:
raise OutboundPolicyError("Outbound URL is malformed") from exc
_validate_parsed_http_url(parsed, port)
return parsed
View on GitHub (pinned to 5e758547a8)