ZhuLinsen/daily_stock_analysis · error · ValueError
Hermes BASE_URL must use http or https
Error message
Hermes BASE_URL must use http or https
What it means
First check in canonicalize_hermes_base_url (src/llm/hermes.py): the Hermes BASE_URL must parse with scheme http or https. Because Hermes is a loopback-only bridge (see later host checks), other schemes are never valid — ftp/file/tcp schemes, or scheme-less strings like '127.0.0.1:8642/v1', are rejected here before host/path validation runs.
Source
Thrown at src/llm/hermes.py:169
def canonicalize_hermes_protocol(protocol: str) -> str:
normalized = (protocol or HERMES_DEFAULT_PROTOCOL).strip().lower() or HERMES_DEFAULT_PROTOCOL
if normalized != HERMES_DEFAULT_PROTOCOL:
raise ValueError("Hermes only supports PROTOCOL=openai")
return HERMES_DEFAULT_PROTOCOL
def canonicalize_hermes_base_url(base_url: str) -> str:
"""Return canonical Hermes base URL or raise ValueError.
Allowed forms are loopback HTTP(S) URLs whose path is exactly /v1 or /v1/.
localhost is canonicalized to 127.0.0.1 to avoid DNS/hosts ambiguity.
"""
raw = (base_url or HERMES_DEFAULT_BASE_URL).strip() or HERMES_DEFAULT_BASE_URL
parsed = urlparse(raw)
if parsed.scheme.lower() not in {"http", "https"}:
raise ValueError("Hermes BASE_URL must use http or https")
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"}:View on GitHub (pinned to 5159bd72e8)
Solutions
- Include the scheme: http://127.0.0.1:8642/v1 (or https on loopback).
- Leave BASE_URL unset to use the default loopback URL if that is the deployment.
Example fix
# before BASE_URL=127.0.0.1:8642/v1 # after BASE_URL=http://127.0.0.1:8642/v1
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
parsed = urlparse(base_url)
assert parsed.scheme.lower() in {"http", "https"}, "include http(s):// in BASE_URL" Type guard
def is_http_url(value: str) -> bool:
return urlparse(value or "").scheme.lower() in {"http", "https"} Try / catch
try:
url = canonicalize_hermes_base_url(cfg.base_url)
except ValueError as exc:
raise ConfigError(f"HERMES BASE_URL invalid: {exc}") from exc Prevention
- Always write the http:// or https:// scheme explicitly
- Leave BASE_URL unset to use the default
- Smoke-test config parsing at startup
When it happens
Trigger: BASE_URL set without a scheme ('127.0.0.1:8642/v1' — urlparse treats '127.0.0.1' as the scheme), or with a non-HTTP scheme. Empty input falls back to HERMES_DEFAULT_BASE_URL ('http://127.0.0.1:8642/v1') and does NOT trigger this.
Common situations: Omitting http:// when configuring the URL (most common); pasting a ws:// or grpc:// endpoint; trailing garbage that breaks URL parsing.
Related errors
- Hermes BASE_URL must include a loopback host
- Hermes BASE_URL must not include userinfo
- Hermes BASE_URL must not include params, query, or fragment
- Hermes BASE_URL path must be /v1
- Hermes BASE_URL path must not contain encoded segments
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/ed6d33ba01ffc9fb.
Report an issue: GitHub.