ZhuLinsen/daily_stock_analysis · critical · RuntimeError

Hermes API key is a masked placeholder and cannot be used fo

Error message

Hermes API key is a masked placeholder and cannot be used for generation

What it means

RuntimeError raised on the Hermes-only direct path when the deployment's api_key in litellm_params matches the masked-secret placeholder pattern (is_masked_secret_placeholder). Masked placeholders appear when secrets were redacted for display/logging (e.g. '****' or a masked token) and such a value is not a usable credential, so generation refuses rather than sending a guaranteed-401 request.

Source

Thrown at src/analyzer.py:2764

        self,
        model: str,
        call_kwargs: Dict[str, Any],
        *,
        config: Config,
        use_channel_router: bool,
        router_model_names: set[str],
    ) -> Any:
        """Dispatch a LiteLLM completion through router or direct fallback."""
        origins = route_deployment_origins(config.llm_model_list, model)
        if origins.is_mixed:
            raise RuntimeError("Hermes/non-Hermes mixed generation route is not supported without deployment-level no-proxy client support")
        if origins.is_hermes_only:
            deployment = origins.hermes_deployments[0]
            params = dict(deployment.get("litellm_params") or {})
            api_key = str(params.get("api_key") or "").strip()
            base_url = str(params.get("api_base") or "").strip()
            if is_masked_secret_placeholder(api_key):
                raise RuntimeError("Hermes API key is a masked placeholder and cannot be used for generation")
            timeout = float(call_kwargs.get("timeout") or 30.0)
            hermes_kwargs = dict(call_kwargs)
            hermes_kwargs["model"] = str(params.get("model") or model)
            hermes_kwargs["stream"] = False
            hermes_kwargs.pop("api_key", None)
            hermes_kwargs.pop("api_base", None)
            with open_hermes_no_proxy_client(api_key=api_key, base_url=base_url, timeout=timeout) as client:
                hermes_kwargs["client"] = client
                return litellm.completion(**hermes_kwargs)

        wire_models = resolve_fallback_litellm_wire_models(model, config.llm_model_list)
        register_fallback_model_pricing(wire_models)
        effective_kwargs = dict(call_kwargs)
        if use_channel_router and self._router and model in router_model_names:
            return self._router.completion(**effective_kwargs)
        if self._router and model == config.litellm_model and not use_channel_router:
            return self._router.completion(**effective_kwargs)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Re-enter the real Hermes API key into the deployment's litellm_params (via env var or secret store, not by pasting into chat).
  2. Check how the config was produced: if it round-tripped through any redaction/sanitization layer, source it from the original secret store instead.
  3. Add a startup assertion that no api_key in llm_model_list satisfies is_masked_secret_placeholder, so the failure surfaces at boot with the deployment name rather than at generation time.

Example fix

# before
litellm_params: {model: ..., api_base: ..., api_key: "********"}  # masked dump re-imported

# after
litellm_params: {model: ..., api_base: ..., api_key: "${HERMES_API_KEY}"}  # resolved from env/secret store at load
Defensive patterns

Strategy: validation

Validate before calling

from src.utils.secrets import is_masked_secret_placeholder  # actual import path per repo

for model in config.llm_model_list:
    for dep in model.get("litellm_params", {}).get("api_key", ""):
        if is_masked_secret_placeholder(str(dep)):
            raise ConfigError("masked API key detected; inject real secrets")

Try / catch

try:
    result = analyzer._dispatch_litellm_completion(...)
except RuntimeError as e:
    if "masked placeholder" in str(e):
        # credential injection failed upstream; fix secret source, never retry as-is
        raise ConfigError("Hermes api_key is masked; re-inject real secret") from e
    raise

Prevention

When it happens

Trigger: LLM generation routed to a Hermes deployment whose litellm_params.api_key is a masked/redacted placeholder string — typically because a sanitized config dump (logs, UI payload, exported settings) was re-imported as the real config, or env substitution never ran and the literal mask text survived.

Common situations: Copying an LLM_MODEL_LIST from a web UI or log output where keys were masked; CI that serializes config with redaction and then reloads it; a secrets-injection step (devkey/env) skipped or failing silently so the placeholder remains.

Related errors


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