mvanhorn/last30days-skill · error · RuntimeError

BRAVE_API_KEY is required when web_backend='brave'

Error message

BRAVE_API_KEY is required when web_backend='brave'

What it means

Raised by the web grounding dispatcher in grounding.py when the resolved web backend is 'brave' but the config dict has no truthy BRAVE_API_KEY. The backend can be forced via web_backend='brave' (config or --web-backend flag), which is an explicit instruction that overrides the automatic keyless/paid-key resolution — so when the matching key is absent the engine fails loudly rather than silently returning zero web results.

Source

Thrown at skills/last30days/scripts/lib/grounding.py:254

        elif config.get("EXA_API_KEY"):
            backend = "exa"
        elif config.get("SERPER_API_KEY"):
            backend = "serper"
        elif config.get("PARALLEL_API_KEY"):
            backend = "parallel"
        elif env.keyless_web_allowed(config):
            # No paid key and the host has no native search -> use the keyless
            # floor. On a native-search host this branch is skipped (the model
            # supplies web results itself), so the engine returns nothing here.
            backend = "keyless"
        else:
            return [], {}
    items: list[dict] = []
    artifact: dict = {}
    if backend == "brave":
        key = config.get("BRAVE_API_KEY")
        if not key:
            raise RuntimeError("BRAVE_API_KEY is required when web_backend='brave'")
        items, artifact = brave_search(query, date_range, key)
    elif backend == "exa":
        key = config.get("EXA_API_KEY")
        if not key:
            raise RuntimeError("EXA_API_KEY is required when web_backend='exa'")
        items, artifact = exa_search(query, date_range, key)
    elif backend == "serper":
        key = config.get("SERPER_API_KEY")
        if not key:
            raise RuntimeError("SERPER_API_KEY is required when web_backend='serper'")
        items, artifact = serper_search(query, date_range, key)
    elif backend == "parallel":
        key = config.get("PARALLEL_API_KEY")
        if not key:
            raise RuntimeError("PARALLEL_API_KEY is required when web_backend='parallel'")
        items, artifact = parallel_search(query, date_range, key)
    elif backend == "keyless":
        items, artifact = web_search_keyless.keyless_search(query, date_range, config)

View on GitHub (pinned to c7460f6114)

Solutions

  1. Set BRAVE_API_KEY in the environment or the last30days .env file (see env.py auth patterns) and re-run.
  2. If you did not intend to force Brave, remove the web_backend='brave' setting so the engine falls back to automatic backend resolution (paid keys present, else keyless floor).
  3. Verify with: python -c "from last30days.scripts.lib.env import load_config; print(bool(load_config().get('BRAVE_API_KEY')))" or an equivalent config dump that masks values.

Example fix

# before
export WEB_BACKEND=brave   # no BRAVE_API_KEY anywhere

# after
echo 'BRAVE_API_KEY=<your-key>' >> ~/.config/last30days/.env
export WEB_BACKEND=brave
Defensive patterns

Strategy: validation

Validate before calling

from skills.last30days.scripts.lib.env import load_config  # project env loader

config = load_config()
if config.get("web_backend") == "brave":
    assert config.get("BRAVE_API_KEY"), "BRAVE_API_KEY must be set for brave backend"

Try / catch

try:
    grounding.web_search_with_backend(query, config)
except RuntimeError as e:
    if 'BRAVE_API_KEY' in str(e):
        # config error, not transient: fix env or unset web_backend
        ...

Prevention

When it happens

Trigger: Setting web_backend='brave' in config/env/CLI while BRAVE_API_KEY is unset, empty, or whitespace; the key exists in a different .env than the one loaded; typo in the variable name (BRAVE_KEY, BRAVEAPI_KEY).

Common situations: Config is copied between machines without the secret; the key is defined in the shell but the engine runs from a harness (Claude Code, cron) that does not inherit it; LAST30DAYS .env file lives in a non-default location.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/d48714380687fca7. Report an issue: GitHub.