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
- Set BRAVE_API_KEY in the environment or the last30days .env file (see env.py auth patterns) and re-run.
- 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).
- 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
- Only force web_backend='brave' on machines whose .env carries BRAVE_API_KEY.
- Prefer leaving web_backend unset so automatic resolution never picks a keyless paid backend.
- Add a startup config assertion for the backend/key pair in scripts that pin a backend.
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
- EXA_API_KEY is required when web_backend='exa'
- SERPER_API_KEY is required when web_backend='serper'
- PARALLEL_API_KEY is required when web_backend='parallel'
- Unsupported web backend: {backend!r}
- Gemini HTTP {exc.code}: {detail}
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/d48714380687fca7.
Report an issue: GitHub.