Panniantong/Agent-Reach · error · TranscribeError
SSRF blocked: URL host is invalid
Error message
SSRF blocked: URL host is invalid
What it means
Raised by _assert_safe_public_url (transcribe.py:240-243) when the extracted hostname fails IDNA encoding (raw_host.encode('idna') raises UnicodeError). Hosts with disallowed Unicode codepoints, empty labels, or overlong labels cannot be safely canonicalized, so the guard treats them as invalid rather than passing ambiguous bytes to yt-dlp.
Source
Thrown at agent_reach/transcribe.py:243
normalized_url = url
parsed = urlparse(url)
if parsed.scheme not in {"http", "https"}:
raise TranscribeError("SSRF blocked: only public http(s) URLs are allowed")
raw_authority = normalized_url.split("://", 1)[1]
raw_authority = raw_authority.split("/", 1)[0]
raw_authority = raw_authority.split("?", 1)[0]
raw_authority = raw_authority.split("#", 1)[0]
if "\\" in raw_authority or "%" in raw_authority:
raise TranscribeError("SSRF blocked: encoded or ambiguous URL host")
raw_host = (parsed.hostname or "").strip().rstrip(".")
if not raw_host:
raise TranscribeError("SSRF blocked: URL host is missing")
try:
host = raw_host.encode("idna").decode("ascii").lower().rstrip(".")
except UnicodeError:
raise TranscribeError("SSRF blocked: URL host is invalid") from None
if host in _BLOCKED_HOSTS or host.endswith(".localhost"):
raise TranscribeError("SSRF blocked: internal host is not allowed")
if _is_private_ip(host):
raise TranscribeError("SSRF blocked: private/internal IP is not allowed")
def download_audio(url: str, out_dir: Path) -> Path:
"""Download audio with yt-dlp into out_dir; return the resulting file path."""
_assert_safe_public_url(url)
_require("yt-dlp")
template = out_dir / "source.%(ext)s"
_run(
[
"yt-dlp",
"-x",
"--audio-format",
"m4a",
"--audio-quality",View on GitHub (pinned to 93ae1d18c3)
Solutions
- Clean the host: strip zero-width/whitespace characters, normalize to NFC, replace typographic dashes with '-'
- Use the punycode form of IDN hosts (e.g. 'xn--bcher-kva.example' instead of 'bücher.example' if encoding keeps failing)
- Validate hosts against a strict regex (^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$) before calling transcribe()
Example fix
# before
url = "https://example.com/audio.mp3" # zero-width space -> SSRF blocked: URL host is invalid
# after
import unicodedata
host = unicodedata.normalize("NFKC", host).replace("\u200b", "").replace("\u2013", "-")
url = f"https://{host}/audio.mp3" Defensive patterns
Strategy: validation
Validate before calling
import unicodedata
def host_encodes_idna(host: str) -> bool:
host = host.strip().rstrip(".")
try:
host.encode("idna")
return True
except UnicodeError:
return False
def clean_host(host: str) -> str:
return (unicodedata.normalize("NFKC", host)
.replace("\u200b", "").replace("\u2013", "-").replace(" ", "")) Try / catch
from agent_reach.transcribe import TranscribeError
try:
transcribe(url)
except TranscribeError as e:
if "URL host is invalid" in str(e):
return transcribe(url_with_cleaned_host(url)) # NFKC-normalize, strip invisibles
raise Prevention
- Normalize hostnames to NFKC and strip zero-width characters before use
- Use punycode for internationalized domains
- Validate hosts against a strict hostname regex at input boundaries
When it happens
Trigger: Hostnames containing characters IDNA rejects: 'https://exa mple.com/' (space), 'https://example.com/' (zero-width char), labels longer than 63 chars, consecutive dots ('example..com'), or non-NFC Unicode that str.encode('idna') refuses.
Common situations: Copy-paste of internationalized domains with stray invisible characters; LLM-generated URLs with typographic dashes (en/em dash) instead of ASCII hyphens; corrupted strings from upstream data sources.
Related errors
- only the V2EX HTTPS API is allowed
- SSRF blocked: only public http(s) URLs are allowed
- SSRF blocked: encoded or ambiguous URL host
- SSRF blocked: internal host is not allowed
- gh hosts.yml 无法安全读取
AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14).
Data as JSON: /api/errors/98616db5bf353594.
Report an issue: GitHub.