{"record":{"id":"f9e54d905daa6d43","repo":"nexu-io/open-design","slug":"exa-api-key-is-required-when-web-backend-exa","errorCode":null,"errorMessage":"EXA_API_KEY is required when web_backend='exa'","messagePattern":"EXA_API_KEY is required when web_backend='exa'","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"design-templates/last30days/scripts/lib/grounding.py","lineNumber":216,"sourceCode":"        if config.get(\"BRAVE_API_KEY\"):\n            backend = \"brave\"\n        elif config.get(\"EXA_API_KEY\"):\n            backend = \"exa\"\n        elif config.get(\"SERPER_API_KEY\"):\n            backend = \"serper\"\n        elif config.get(\"PARALLEL_API_KEY\"):\n            backend = \"parallel\"\n        else:\n            return [], {}\n    if backend == \"brave\":\n        key = config.get(\"BRAVE_API_KEY\")\n        if not key:\n            raise RuntimeError(\"BRAVE_API_KEY is required when web_backend='brave'\")\n        return brave_search(query, date_range, key)\n    if backend == \"exa\":\n        key = config.get(\"EXA_API_KEY\")\n        if not key:\n            raise RuntimeError(\"EXA_API_KEY is required when web_backend='exa'\")\n        return exa_search(query, date_range, key)\n    if backend == \"serper\":\n        key = config.get(\"SERPER_API_KEY\")\n        if not key:\n            raise RuntimeError(\"SERPER_API_KEY is required when web_backend='serper'\")\n        return serper_search(query, date_range, key)\n    if backend == \"parallel\":\n        key = config.get(\"PARALLEL_API_KEY\")\n        if not key:\n            raise RuntimeError(\"PARALLEL_API_KEY is required when web_backend='parallel'\")\n        return parallel_search(query, date_range, key)\n    if backend != \"none\":\n        raise ValueError(f\"Unsupported web backend: {backend!r}\")\n    return [], {}\n\n\n# ---------------------------------------------------------------------------\n# Helpers","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/design-templates/last30days/scripts/lib/grounding.py#L198-L234","documentation":"Same dispatcher guard as 532 but for the 'exa' branch: backend resolved to 'exa' (forced, or auto because EXA_API_KEY was the first truthy key) yet config.get('EXA_API_KEY') is falsy at the brave/exa/serper/parallel dispatch block. RuntimeError names the missing key and the backend that requires it.","triggerScenarios":"`--web-backend exa` (or LAST30DAYS_WEB_BACKEND=exa) with no EXA_API_KEY in the resolved config.","commonSituations":"Forcing exa without the key; .env not loaded; CI secret missing; key renamed.","solutions":["Export EXA_API_KEY and rerun.","Switch to a backend you have a key for, or use `--web-backend auto` / `--web-backend none`."],"exampleFix":"# before\npython3.12 last30days.py --topic x --web-backend exa   # EXA_API_KEY unset\n# after\nexport EXA_API_KEY=...\npython3.12 last30days.py --topic x --web-backend exa","handlingStrategy":"validation","validationCode":"if web_backend == 'exa' and not config.get('EXA_API_KEY'):\n    raise RuntimeError(\"EXA_API_KEY is required when web_backend='exa'\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer `--web-backend auto`.","Load .env before resolving config so EXA_API_KEY is visible."],"tags":["config","api-key","python","web-search","grounding"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}