ZhuLinsen/daily_stock_analysis · error · ValueError
Hermes BASE_URL path must not contain encoded segments
Error message
Hermes BASE_URL path must not contain encoded segments
What it means
Anti-evasion check in canonicalize_hermes_base_url: although the decoded path matched /v1, the RAW path must also be the plain literal (quote(decoded, safe='/') must equal the raw path modulo the trailing slash). This catches percent-encoded variants like '/%76%31' or '/v%31' that decode to /v1 — the bridge only accepts the unencoded literal, both to block URL-encoding tricks that could bypass path review and to keep the canonical URL deterministic.
Source
Thrown at src/llm/hermes.py:182
"""
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"}:
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=""))
View on GitHub (pinned to 5159bd72e8)
Solutions
- Write the path literally: 'http://127.0.0.1:8642/v1' with no percent-encoding.
- Find and disable whatever layer is re-encoding the URL (proxy, config manager) if you never typed encoded characters.
Example fix
# before BASE_URL=http://127.0.0.1:8642/%76%31 # after BASE_URL=http://127.0.0.1:8642/v1
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
raw_path = urlparse(base_url).path or ""
assert "%" not in raw_path and raw_path.rstrip("/") == "/v1", "use the literal /v1 path" Type guard
def is_plain_v1_path(value: str) -> bool:
raw = urlparse(value).path or ""
return "%" not in raw and raw.rstrip("/") == "/v1" Try / catch
try:
url = canonicalize_hermes_base_url(cfg.base_url)
except ValueError as exc:
raise ConfigError(str(exc)) from exc Prevention
- Never percent-encode the path; type /v1 literally
- Check proxies/config managers that re-encode URLs
- Treat encoded-path configs as a security smell, not a workaround
When it happens
Trigger: BASE_URL='http://127.0.0.1:8642/%76%31' or 'http://127.0.0.1:8642/v%31/' — anything whose raw path differs from its quoted-decoded form while still decoding to /v1. Plain '/v1' and '/v1/' never trigger this.
Common situations: URL-normalization tooling or proxies rewriting the path with encoding; hand-crafted URLs from security testing; configs passed through layers that percent-encode.
Related errors
- Hermes BASE_URL must not include userinfo
- Hermes BASE_URL must use http or https
- Hermes BASE_URL must include a loopback host
- Hermes BASE_URL must not include params, query, or fragment
- Hermes BASE_URL path must be /v1
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/00f8176354492482.
Report an issue: GitHub.