{"record":{"id":"547d9c900a08b0f9","repo":"nexu-io/open-design","slug":"requests-library-not-available-for-webhook-deliver","errorCode":null,"errorMessage":"requests library not available for webhook delivery","messagePattern":"requests library not available for webhook delivery","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"design-templates/last30days/scripts/watchlist.py","lineNumber":62,"sourceCode":"\n\ndef _format_delivery_message(topic: str, counts: dict, mode: str) -> str:\n    \"\"\"Format notification message based on delivery mode.\"\"\"\n    new = counts.get(\"new\", 0)\n    updated = counts.get(\"updated\", 0)\n\n    if mode == \"announce\":\n        return f\"📰 *last30days update: {topic}*\\n{new} new, {updated} updated\"\n    elif mode == \"silent\":\n        return f\"last30days: {new} new findings for '{topic}'\"\n    else:\n        return f\"last30days: Research complete for '{topic}'\"\n\n\ndef _send_slack_webhook(url: str, text: str) -> None:\n    \"\"\"POST to Slack incoming webhook.\"\"\"\n    if not requests:\n        raise RuntimeError(\"requests library not available for webhook delivery\")\n\n    response = requests.post(\n        url,\n        json={\"text\": text},\n        headers={\"Content-Type\": \"application/json\"},\n        timeout=10,\n    )\n    response.raise_for_status()\n\n\ndef _send_generic_webhook(url: str, text: str) -> None:\n    \"\"\"POST JSON payload to generic webhook.\"\"\"\n    if not requests:\n        raise RuntimeError(\"requests library not available for webhook delivery\")\n\n    response = requests.post(\n        url,\n        json={","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/design-templates/last30days/scripts/watchlist.py#L44-L80","documentation":"Raised in _send_slack_webhook (and _send_generic_webhook) when the module-level `requests` symbol is None. watchlist.py imports requests in a try/except ImportError block, setting requests=None on failure; the webhook senders gate on `if not requests` and refuse to deliver because there is no HTTP client to POST with.","triggerScenarios":"Running watchlist delivery while the `requests` package is not installed in the active Python environment. The pipeline's own http module may use urllib, but watchlist delivery specifically requires the third-party requests library.","commonSituations":"Slim container/venv that installed only the pipeline deps but not requests. Running watchlist.py under a different interpreter than the one that pip-installed dependencies. requests removed during a dependency cleanup.","solutions":["Install requests in the environment: pip install requests (or add it to the project requirements).","If you cannot install requests, disable webhook delivery (clear the delivery_channel setting) so watchlist skips the send path entirely.","Note the outer _deliver_findings already wraps the call in try/except and prints to stderr, so a missing requests will not crash the research run, only the notification."],"exampleFix":"# before (requests missing)\n# delivery fails: 'requests library not available for webhook delivery'\n\n# after\npip install requests\n# or, in watchlist config, leave delivery_channel empty to skip delivery","handlingStrategy":"validation","validationCode":"def can_deliver_webhook():\n    import importlib.util\n    return importlib.util.find_spec(\"requests\") is not None\n\n# before scheduling delivery:\nif not can_deliver_webhook():\n    print(\"requests not installed; webhook delivery disabled\", file=sys.stderr)","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Add `requests` to the project requirements so watchlist delivery always works.","If requests is optional, clear delivery_channel in config so the send path is never reached.","Rely on _deliver_findings' outer try/except so a missing requests does not crash the research run, only the notification."],"tags":["dependencies","webhook","optional-import","notifications"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}