mvanhorn/last30days-skill · error · RuntimeError
SERPER_API_KEY is required when web_backend='serper'
Error message
SERPER_API_KEY is required when web_backend='serper'
What it means
Raised by grounding.py when web_backend resolves to 'serper' but SERPER_API_KEY is not present in the config dict. Like the other paid-key backends, serper is only ever entered by explicit selection, so the dispatcher treats a missing key as a configuration error (RuntimeError) instead of degrading to no results.
Source
Thrown at skills/last30days/scripts/lib/grounding.py:264
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)
elif backend != "none":
raise ValueError(f"Unsupported web backend: {backend!r}")
else:
return [], {}
if items and not _reddit_excluded(config):
# Reddit enrichment is a best-effort secondary fetch on already-retrieved
# web results. Isolate its HTTP failures in a throwaway capture sink so a
# reddit.com fetch failure (e.g. a 403 on a datacenter IP) is not
# attributed to the web/grounding source itself — which would otherwise
# discard the successfully retrieved results and report the source failed.View on GitHub (pinned to c7460f6114)
Solutions
- Add SERPER_API_KEY to the environment or last30days .env and re-run.
- If the key is not available, remove the explicit web_backend setting and let automatic resolution choose an available backend.
- Double-check spelling: it must be SERPER_API_KEY exactly.
Example fix
# before # .env has SERPER_KEY=... # after # .env has SERPER_API_KEY=<your-key>
Defensive patterns
Strategy: validation
Validate before calling
config = load_config()
if config.get("web_backend") == "serper":
assert config.get("SERPER_API_KEY"), "SERPER_API_KEY must be set for serper backend" Try / catch
try:
grounding.web_search_with_backend(query, config)
except RuntimeError as e:
if 'SERPER_API_KEY' in str(e):
# fix env spelling (SERPER_API_KEY, not SERPER_KEY) or unset the backend
... Prevention
- Check the exact spelling SERPER_API_KEY when setup fails.
- Keep one .env as the single source of backend + key pairs; do not split across files.
- Test with a trivial query right after configuring a new backend.
When it happens
Trigger: web_backend='serper' with SERPER_API_KEY unset/empty; key present only in the interactive shell but the engine is spawned by an agent gateway with a limited PATH/env; stale .env cached from before the key was added.
Common situations: User copies a recommended config that names serper.dev but never registers a key; the .env was written to the wrong directory (not ~/.config/last30days or LAST30DAYS_STORE); a typo like SERPER_KEY.
Related errors
- BRAVE_API_KEY is required when web_backend='brave'
- EXA_API_KEY is required when web_backend='exa'
- 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/f958214932185760.
Report an issue: GitHub.