nexu-io/open-design · error · ValueError
Unsupported web backend: {backend!r}
Error message
Unsupported web backend: {backend!r} What it means
Final guard in web_search(): if backend is not one of auto/brave/exa/serper/parallel/none, raise ValueError(f"Unsupported web backend: {backend!r}"). Unlike the RuntimeError siblings this is a ValueError (programming/contract error, not a missing secret). The dispatcher normalizes 'auto' before this point, so reaching here means an explicit, unrecognized backend string was forced.
Source
Thrown at design-templates/last30days/scripts/lib/grounding.py:229
raise RuntimeError("BRAVE_API_KEY is required when web_backend='brave'")
return brave_search(query, date_range, key)
if backend == "exa":
key = config.get("EXA_API_KEY")
if not key:
raise RuntimeError("EXA_API_KEY is required when web_backend='exa'")
return exa_search(query, date_range, key)
if backend == "serper":
key = config.get("SERPER_API_KEY")
if not key:
raise RuntimeError("SERPER_API_KEY is required when web_backend='serper'")
return serper_search(query, date_range, key)
if backend == "parallel":
key = config.get("PARALLEL_API_KEY")
if not key:
raise RuntimeError("PARALLEL_API_KEY is required when web_backend='parallel'")
return parallel_search(query, date_range, key)
if backend != "none":
raise ValueError(f"Unsupported web backend: {backend!r}")
return [], {}
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _normalize_date(value: object) -> str | None:
if value is None:
return None
parsed = dates.parse_date(str(value).strip())
if not parsed:
return None
return parsed.date().isoformat()
def _serper_date_param(iso_date: str) -> str:
"""Convert YYYY-MM-DD to MM/DD/YYYY for Serper tbs parameter."""View on GitHub (pinned to 5be4028344)
Solutions
- Use one of: auto, none, brave, exa, serper, parallel.
- Spell it lowercase — the comparison is exact.
- Prefer 'auto' to let the code choose based on which key is set.
Example fix
# before python3.12 last30days.py --topic x --web-backend Bing # after python3.12 last30days.py --topic x --web-backend auto
Defensive patterns
Strategy: type-guard
Validate before calling
SUPPORTED_BACKENDS = {'auto', 'none', 'brave', 'exa', 'serper', 'parallel'}
if web_backend not in SUPPORTED_BACKENDS:
raise ValueError(f'web_backend must be one of {sorted(SUPPORTED_BACKENDS)}, got {web_backend!r}') Type guard
def is_supported_backend(name: object) -> bool:
return isinstance(name, str) and name in {'auto','none','brave','exa','serper','parallel'} Prevention
- Lowercase backend names before comparison; the dispatcher is case-sensitive.
- Default to 'auto' so the code selects based on available keys.
When it happens
Trigger: `--web-backend bing`, `--web-backend Google`, `--web-backend ""` after the auto check, or any value not in the supported set passed programmatically.
Common situations: Typo in the backend name; case mismatch (the dispatcher does not lowercase); a stale value from old docs; passing an empty string explicitly.
Related errors
- BRAVE_API_KEY is required when web_backend='brave'
- 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'
- Unknown search source: {source}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/4df722cdd408b5e1.
Report an issue: GitHub.