ZhuLinsen/daily_stock_analysis · error · ValueError
Hermes BASE_URL must point to 127.0.0.1, localhost, or [::1]
Error message
Hermes BASE_URL must point to 127.0.0.1, localhost, or [::1]
What it means
Raised while canonicalizing the Hermes LLM base URL. The validation function only allows loopback hosts (127.0.0.1, localhost, [::1]) because Hermes is a local, OpenAI-compatible HTTP endpoint; any remote hostname or IP is rejected before the client is built. The error surfaces as a ValueError from URL parsing at config-load time.
Source
Thrown at src/llm/hermes.py:188
if not parsed.netloc or not parsed.hostname:
raise ValueError("Hermes BASE_URL must include a loopback host")
if parsed.username or parsed.password:
raise ValueError("Hermes BASE_URL must not include userinfo")
if parsed.params or parsed.query or parsed.fragment:
raise ValueError("Hermes BASE_URL must not include params, query, or fragment")
raw_path = parsed.path or ""
decoded_path = unquote(raw_path)
if decoded_path not in {"/v1", "/v1/"}:
raise ValueError("Hermes BASE_URL path must be /v1")
if quote(decoded_path, safe="/") != raw_path.rstrip("/") and raw_path not in {"/v1", "/v1/"}:
raise ValueError("Hermes BASE_URL path must not contain encoded segments")
hostname = parsed.hostname.strip().lower()
if hostname == "localhost":
hostname = "127.0.0.1"
elif hostname not in {"127.0.0.1", "::1"}:
raise ValueError("Hermes BASE_URL must point to 127.0.0.1, localhost, or [::1]")
try:
port = parsed.port
except ValueError as exc:
raise ValueError("Hermes BASE_URL contains an invalid port") from exc
netloc = f"[{hostname}]" if ":" in hostname else hostname
if port is not None:
netloc = f"{netloc}:{port}"
return urlunparse(parsed._replace(netloc=netloc, path="/v1", params="", query="", fragment=""))
def canonicalize_hermes_model_ref(raw_model: str) -> HermesModelRef:
"""Return the canonical DSA route and LiteLLM wire model for Hermes.
Hermes is OpenAI-compatible over local HTTP, so both route identity and
outbound wire model use LiteLLM's openai/ namespace. The display label is
only UI metadata and must not be used for routing or provider detection.View on GitHub (pinned to 5159bd72e8)
Solutions
- Set HERMES_BASE_URL (or the equivalent env/config key) to a loopback URL with the /v1 path, e.g. http://127.0.0.1:8080/v1
- If Hermes runs in Docker or on another host, port-forward it to localhost (e.g. docker -p 127.0.0.1:8080:8080 or ssh -L) so the URL can stay loopback
- Replace 0.0.0.0 with 127.0.0.1 in the URL — 0.0.0.0 is a bind address, not a valid connect target under this policy
- Verify the path component is exactly /v1 or /v1/ and contains no percent-encoded segments, since those raise adjacent validation errors
Example fix
# before HERMES_BASE_URL=http://0.0.0.0:8080/v1 # after HERMES_BASE_URL=http://127.0.0.1:8080/v1
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
def is_valid_hermes_base_url(url: str) -> bool:
try:
parsed = urlsplit(url)
except ValueError:
return False
host = (parsed.hostname or "").strip().lower()
if host not in {"127.0.0.1", "::1", "localhost"}:
return False
from urllib.parse import unquote
path = unquote(parsed.path or "")
return path in {"/v1", "/v1/"} Try / catch
try:
url = canonicalize_hermes_base_url(raw)
except ValueError as exc:
raise ConfigError(f"HERMES_BASE_URL invalid: {exc}") from exc Prevention
- Validate HERMES_BASE_URL once at startup and fail fast with the allowed format in the message
- Never use 0.0.0.0 as a connect address; bind wide, connect to 127.0.0.1
- Keep a single source of truth for the URL in env config, not hardcoded strings
When it happens
Trigger: Calling the Hermes URL canonicalizer with a BASE_URL whose hostname is anything other than 127.0.0.1, localhost, or ::1 — e.g. http://0.0.0.0:8080/v1, http://192.168.1.5:8080/v1, https://api.example.com/v1, or a bare host like http://hermes:8080/v1. Note that 0.0.0.0 is explicitly NOT in the allowlist even though it is often used for local listeners.
Common situations: Pointing Hermes at a machine reachable on the LAN instead of localhost; using 0.0.0.0 (the bind address) as the connect address; copying a docker-compose service name into BASE_URL; leaving a placeholder cloud URL in .env after switching from a remote provider to the local Hermes backend.
Related errors
- Hermes BASE_URL contains an invalid port
- Responses API surface requires a normalized openai/<model> r
- LLM route aliases cannot mix API surfaces: {sorted(surface_c
- Hermes only supports PROTOCOL=openai
- unsafe_config
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/d24f0910f2e30336.
Report an issue: GitHub.