ZhuLinsen/daily_stock_analysis · error · ValueError

Hermes BASE_URL contains an invalid port

Error message

Hermes BASE_URL contains an invalid port

What it means

Raised when urlsplit's parsed.port property throws while canonicalizing the Hermes BASE_URL. Python's port accessor raises ValueError when the port segment is non-numeric or outside 0-65535; this code wraps it into a clearer Hermes-specific ValueError. It is a strict config-format error raised before any network activity.

Source

Thrown at src/llm/hermes.py:193

        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.
    """

    display_model = str(raw_model or "").strip() or HERMES_DEFAULT_MODEL
    if display_model.startswith("openai/"):
        canonical = display_model

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Correct the port to a plain integer between 1 and 65535, e.g. http://127.0.0.1:8080/v1
  2. Remove stray characters, spaces, or trailing punctuation from the netloc portion of the URL
  3. If the port comes from variable interpolation, print/render the final URL once during setup to catch substitution bugs
  4. Omit the port entirely when Hermes listens on the default port 80 so the parser never touches a port segment

Example fix

# before
HERMES_BASE_URL=http://127.0.0.1:8080x/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 has_valid_port(url: str) -> bool:
    try:
        parsed = urlsplit(url)
        parsed.port  # raises ValueError on bad port
        return True
    except ValueError:
        return False

Try / catch

try:
    url = canonicalize_hermes_base_url(raw)
except ValueError as exc:
    if "port" in str(exc):
        log.error("Fix HERMES_BASE_URL port: must be 1-65535")
    raise

Prevention

When it happens

Trigger: A BASE_URL like http://127.0.0.1:8080x/v1 (non-numeric port), http://127.0.0.1:99999/v1 (port above 65535), http://127.0.0.1:/v1 with stray characters, or a copy-paste artifact such as http://127.0.0.1:8080 /v1 that leaves a malformed netloc.

Common situations: Typos when hand-editing .env; port copied together with a colon or whitespace; using a URL with a service-port placeholder like <port> left unsubstituted; environment variable interpolation producing an empty-but-present malformed suffix.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/2c9ff2e3eed35873. Report an issue: GitHub.