{"record":{"id":"d9dc5f8becaa1326","repo":"mvanhorn/last30days-skill","slug":"parallel-api-key-is-required-when-web-backend-par","errorCode":null,"errorMessage":"PARALLEL_API_KEY is required when web_backend='parallel'","messagePattern":"PARALLEL_API_KEY is required when web_backend='parallel'","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/grounding.py","lineNumber":269,"sourceCode":"    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        items, artifact = brave_search(query, date_range, key)\n    elif 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        items, artifact = exa_search(query, date_range, key)\n    elif 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        items, artifact = serper_search(query, date_range, key)\n    elif 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        items, artifact = parallel_search(query, date_range, key)\n    elif backend == \"keyless\":\n        items, artifact = web_search_keyless.keyless_search(query, date_range, config)\n    elif backend != \"none\":\n        raise ValueError(f\"Unsupported web backend: {backend!r}\")\n    else:\n        return [], {}\n    if items and not _reddit_excluded(config):\n        # Reddit enrichment is a best-effort secondary fetch on already-retrieved\n        # web results. Isolate its HTTP failures in a throwaway capture sink so a\n        # reddit.com fetch failure (e.g. a 403 on a datacenter IP) is not\n        # attributed to the web/grounding source itself — which would otherwise\n        # discard the successfully retrieved results and report the source failed.\n        with http.capture_failures():\n            items = _enrich_reddit_items(items)\n    return items, artifact\n\n","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/grounding.py#L251-L287","documentation":"Raised by grounding.py when the resolved backend is 'parallel' (search.parallel.ai) but PARALLEL_API_KEY is missing from config. Same contract as brave/exa/serper: an explicitly chosen paid backend requires its key, otherwise RuntimeError. The 'keyless' and 'none' backends are the only paths that never need a key.","triggerScenarios":"web_backend='parallel' with PARALLEL_API_KEY unset; the key was rotated/revoked and removed from .env but the backend flag remained; env var defined with surrounding quotes or spaces making it empty after parsing.","commonSituations":"Adopting the Parallel backend after seeing it in CONFIGURATION.md without completing signup; orchestrators that strip unknown env vars; the flag set globally in a shared config while the key is per-machine.","solutions":["Set PARALLEL_API_KEY in the environment or last30days .env and re-run.","Or drop the explicit web_backend='parallel' selection to fall back to automatic resolution.","Validate the config as the engine sees it (mask values) to confirm the key resolves."],"exampleFix":"# before\nexport WEB_BACKEND=parallel   # no PARALLEL_API_KEY\n\n# after\nexport PARALLEL_API_KEY=<your-key>\nexport WEB_BACKEND=parallel","handlingStrategy":"validation","validationCode":"config = load_config()\nif config.get(\"web_backend\") == \"parallel\":\n    assert config.get(\"PARALLEL_API_KEY\"), \"PARALLEL_API_KEY must be set for parallel backend\"","typeGuard":null,"tryCatchPattern":"try:\n    grounding.web_search_with_backend(query, config)\nexcept RuntimeError as e:\n    if 'PARALLEL_API_KEY' in str(e):\n        # env/config fix; do not retry — it will fail identically\n        ...","preventionTips":["Register the Parallel key before flipping web_backend to 'parallel'.","Rotate keys by editing the .env in place, never by deleting the entry while the flag remains.","Log a masked key presence check at app start for every forced backend."],"tags":["config","api-key","web-search","parallel"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}