datawhalechina/hello-agents · critical · RuntimeError

LLM client is not configured. Check .env.

Error message

LLM client is not configured. Check .env.

What it means

LLMClient.chat raises RuntimeError when the client is not fully configured — model_name, api_key, and base_url must all be truthy per is_enabled(). It is a fail-fast guard so the code never sends a chat request destined to be rejected by the provider.

Source

Thrown at Co-creation-projects/huailishang-AgentPlatformBase/agents/rss_digest/src/rss_digest/llm.py:22

from urllib.request import Request, urlopen
import json
import re


@dataclass(slots=True)
class LLMClient:
    model_name: str
    api_key: str
    base_url: str
    timeout_seconds: int
    json_mode: bool = True

    def is_enabled(self) -> bool:
        return bool(self.model_name and self.api_key and self.base_url)

    def chat(self, system_prompt: str, user_prompt: str) -> str:
        if not self.is_enabled():
            raise RuntimeError("LLM client is not configured. Check .env.")

        payload = {
            "model": self.model_name,
            "temperature": 0.2,
            "messages": [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt},
            ],
        }
        if self.json_mode:
            payload["response_format"] = {"type": "json_object"}

        body = json.dumps(payload).encode("utf-8")
        request = Request(
            f"{self.base_url}/chat/completions",
            data=body,
            headers={
                "Authorization": f"Bearer {self.api_key}",

View on GitHub (pinned to 606a07d341)

Solutions

  1. Create/fix .env with all three values the client reads (model name, API key, base URL) and ensure it is loaded at startup.
  2. Verify with a quick check: print/app-log client.is_enabled() before first use, or `env | grep -i llm` in the deploy shell.
  3. In Docker/CI, pass the variables as environment variables or explicitly COPY .env in the Dockerfile.
  4. If configuration is genuinely optional, gate chat() calls behind is_enabled() and skip LLM-backed features.

Example fix

# before
summary = client.chat(system, user)  # RuntimeError if .env missing

# after
if not client.is_enabled():
    raise RuntimeError("LLM disabled: set model/API key/base URL in .env before enabling digests")
summary = client.chat(system, user)
Defensive patterns

Strategy: validation

Validate before calling

if not client.is_enabled():
    raise SystemExit("LLM client incomplete: set model, API key, and base URL in .env")
result = client.chat(system_prompt, user_prompt)

Type guard

def llm_ready(client: LLMClient) -> bool:
    return client.is_enabled()  # model_name and api_key and base_url all set

Try / catch

try:
    reply = client.chat(system_prompt, user_prompt)
except RuntimeError as e:
    if "not configured" in str(e):
        logger.error("LLM config missing; digest generation skipped")
        return  # feature-flag style skip
    raise

Prevention

When it happens

Trigger: Calling chat() when any of LLM_MODEL / API key / base URL was never set — typically because the .env file is missing, not loaded, or the variable names don't match what the settings loader expects.

Common situations: Deploying without copying .env.example to .env; running in Docker/CI where .env is not copied into the image; pydantic-settings BaseSettings not pointed at the .env path; renaming env vars between environments.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/e83aa0969817259f. Report an issue: GitHub.