iflytek/astron-agent · error · OutboundPolicyError
Outbound URL must not include a fragment
Error message
Outbound URL must not include a fragment
What it means
URL fragments (#anchor) are forbidden in outbound tool URLs. Fragments are client-side only and not sent to the server per spec, but HTTP clients and intermediate parsers handle them inconsistently, so the guard rejects them outright to eliminate the ambiguity.
Solutions
- Remove the #fragment portion from the URL.
- If '#' is part of a data value, percent-encode it: urllib.parse.quote('#') → %23.
- Strip programmatically: url.split('#', 1)[0].
- Fix the copy/paste or template step that introduced the anchor.
Example fix
// before
url = "https://api.example.com/items#section-2"
// after
url = "https://api.example.com/items".split('#', 1)[0] Defensive patterns
Strategy: validation
Validate before calling
def has_no_fragment(url: str) -> bool:
return not urlsplit(url).fragment Try / catch
try:
client.get(url)
except OutboundPolicyError:
url = url.split('#', 1)[0]
client.get(url) Prevention
- Strip '#' fragments when copying URLs from browsers into config
- Percent-encode '#' inside data values with urllib.parse.quote
- Keep fragments client-side only; server APIs should never need them
When it happens
Trigger: _parse_http_url receives a URL with a '#...' suffix — e.g. a link copied from a browser address bar including an anchor, or a template where an unescaped '#' appears in a path/query value.
Common situations: Copying https://site/docs#section into tool config; unencoded '#' inside a query value; SPA hash routes.
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
- HTTPClientError
- MODEL_URL_CHECK_FAILED
- MODEL_URL_CHECK_FAILED
- MODEL_URL_ILLEGAL_FAILED
- Only HTTP and HTTPS tool URLs are allowed
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/c54c33fd61c3b8aa.
Report an issue: GitHub.
Appendix: source
Thrown at core/plugin/link/infra/tool_exector/ssrf_guard.py:210
return parsed
def _validate_url_characters(url: str) -> None:
if any(ord(character) < 0x20 or ord(character) == 0x7F for character in url):
raise OutboundPolicyError("Outbound URL contains control characters")
def _validate_parsed_http_url(parsed: SplitResult, port: Union[int, None]) -> None:
if parsed.scheme.lower() not in _ALLOWED_SCHEMES:
raise OutboundPolicyError("Only HTTP and HTTPS tool URLs are allowed")
if not parsed.hostname:
raise OutboundPolicyError("Outbound URL must include a hostname")
if parsed.username is not None or parsed.password is not None:
raise OutboundPolicyError("Outbound URL must not include user information")
if "\\" in parsed.netloc:
raise OutboundPolicyError("Outbound URL authority is invalid")
if parsed.fragment:
raise OutboundPolicyError("Outbound URL must not include a fragment")
if port is not None and not 1 <= port <= 65535:
raise OutboundPolicyError("Outbound URL port is invalid")
def _parse_networks(raw_value: str, setting_name: str) -> Tuple[IpNetwork, ...]:
networks = []
for entry in raw_value.split(","):
value = entry.strip()
if not value:
continue
try:
networks.append(ipaddress.ip_network(value, strict=False))
except ValueError as exc:
raise OutboundPolicyError(f"Invalid {setting_name} entry") from exc
return tuple(networks)
def _parse_domains(raw_value: str) -> Tuple[str, ...]:View on GitHub (pinned to 5e758547a8)