{"record":{"id":"2926cc8915d20f1a","repo":"mvanhorn/last30days-skill","slug":"exc-reason","errorCode":null,"errorMessage":"{exc.reason}","messagePattern":"\\{exc\\.reason\\}","errorType":"exception","errorClass":"HtmlPublishError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/html_publish.py","lineNumber":57,"sourceCode":"    payload: dict[str, str] = {\"html_content\": html_content}\n    if password is not None:\n        payload[\"password\"] = password\n\n    request = Request(\n        endpoint,\n        data=json.dumps(payload).encode(\"utf-8\"),\n        headers={\"Content-Type\": \"application/json\", \"Accept\": \"application/json\"},\n        method=\"POST\",\n    )\n    open_fn = opener or urlopen\n    try:\n        with open_fn(request, timeout=timeout) as response:\n            body = response.read().decode(\"utf-8\")\n    except HTTPError as exc:\n        detail = exc.read().decode(\"utf-8\", errors=\"replace\")\n        raise HtmlPublishError(_error_message(exc.code, detail)) from exc\n    except URLError as exc:\n        raise HtmlPublishError(str(exc.reason)) from exc\n    except OSError as exc:\n        raise HtmlPublishError(str(exc)) from exc\n\n    try:\n        result = json.loads(body)\n    except json.JSONDecodeError as exc:\n        raise HtmlPublishError(\"publish endpoint returned non-JSON response\") from exc\n    if not isinstance(result, dict):\n        raise HtmlPublishError(\"publish endpoint returned unexpected JSON response\")\n\n    url = result.get(\"url\")\n    if not isinstance(url, str) or not url.startswith(\"https://\"):\n        raise HtmlPublishError(\"publish endpoint response did not include a valid url\")\n    return result\n\n\ndef publish_html_documents(\n    documents: Mapping[str, str],","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/html_publish.py#L39-L75","documentation":"Raised by publish_html when the request fails before getting an HTTP response and the underlying exception is a urllib URLError — the message is str(exc.reason), the inner cause (DNS failure, connection refused, SSL error, timeout during connect). Chained as 'from exc' so the full traceback still shows the URLError.","triggerScenarios":"No network / offline machine; DNS cannot resolve api.ht-ml.app (or the custom endpoint host); a firewall or proxy blocks outbound HTTPS; TLS certificate verification fails; endpoint port closed.","commonSituations":"Running inside a container or CI runner without network egress; corporate MITM proxy with an untrusted CA; airplane-mode laptop; a custom endpoint= pointing at a local server that is not running yet (connection refused).","solutions":["Verify basic connectivity: curl -I https://api.ht-ml.app/v1/sites (or your custom endpoint).","For custom endpoints, confirm the local/remote server is up and the URL scheme/port are right.","In proxied environments, export HTTPS_PROXY and ensure the proxy CA is trusted.","Treat as transient only if the network is flaky; DNS/connection-refused usually means environment, not provider."],"exampleFix":"# before\nresult = publish_html(html)\n\n# after\nimport socket\ntry:\n    result = publish_html(html)\nexcept HtmlPublishError as e:\n    if isinstance(e.__cause__, URLError) and isinstance(e.__cause__.reason, (socket.gaierror, ConnectionRefusedError)):\n        log.warning(\"endpoint unreachable: %s\", e)\n        result = None","handlingStrategy":"retry","validationCode":"import socket\n\ndef endpoint_reachable(host: str, port: int = 443, timeout: float = 5.0) -> bool:\n    try:\n        socket.create_connection((host, port), timeout=timeout).close()\n        return True\n    except OSError:\n        return False","typeGuard":null,"tryCatchPattern":"from urllib.error import URLError\n\ntry:\n    publish_html(html)\nexcept HtmlPublishError as e:\n    if isinstance(e.__cause__, URLError):\n        reason = e.__cause__.reason\n        # DNS failure / refused / SSL -> environment problem; fix network, do not blind-retry\n        log.error(\"publish endpoint unreachable: %s\", reason)","preventionTips":["Check e.__cause__ to classify: gaierror (DNS), ConnectionRefusedError (server down), ssl errors (TLS/CA).","In containers/CI, verify egress to api.ht-ml.app:443 before running publish steps.","Set HTTPS_PROXY and trust the proxy CA in corporate networks."],"tags":["network","dns","html-publish","urllib"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}