{"record":{"id":"238bf13cfde01348","repo":"CoplayDev/unity-mcp","slug":"cannot-reach-url-e","errorCode":null,"errorMessage":"Cannot reach {url}: {e}","messagePattern":"Cannot reach (.+?): (.+?)","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"error","filePath":"Server/src/services/tools/unity_docs.py","lineNumber":100,"sourceCode":"\n\nasync def _fetch_url_full(url: str) -> tuple[int, str, str]:\n    \"\"\"Fetch a URL and return (status_code, body_text, final_url).\n\n    Like _fetch_url but also returns the final URL after any redirects.\n    \"\"\"\n    loop = asyncio.get_running_loop()\n\n    def _do_fetch() -> tuple[int, str, str]:\n        req = Request(url, headers={\"User-Agent\": \"MCPForUnity/1.0\"})\n        try:\n            with urlopen(req, timeout=10) as resp:\n                body = resp.read().decode(\"utf-8\", errors=\"replace\")\n                return (resp.status, body, resp.url)\n        except HTTPError as e:\n            return (e.code, \"\", url)\n        except URLError as e:\n            raise ConnectionError(f\"Cannot reach {url}: {e}\") from e\n\n    return await loop.run_in_executor(None, _do_fetch)\n\n\n# ---------------------------------------------------------------------------\n# HTML parser\n# ---------------------------------------------------------------------------\n\nclass _UnityDocParser(HTMLParser):\n    \"\"\"Extracts structured data from Unity ScriptReference HTML pages.\"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        # Tracking state\n        self._in_subsection = False\n        self._subsection_depth = 0\n        self._subsection_title: str | None = None\n        self._in_signature = False","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/Server/src/services/tools/unity_docs.py#L82-L118","documentation":"Raised by _fetch_url_full (unity_docs.py:100) when urllib's urlopen throws a URLError fetching a Unity ScriptReference page. HTTP-level failures (404, 500) are caught separately as HTTPError and returned as a status code, so this error is strictly transport-level: DNS resolution failure, connection refused, no route to host, or the 10s urlopen timeout expiring with no response.","triggerScenarios":"Calling any unity_docs tool (e.g. get_unity_docs / member lookup) that builds a https://docs.unity3d.com/.../ScriptReference URL and calls _fetch_url/_fetch_url_full while the host has no network path to docs.unity3d.com.","commonSituations":"Sandboxed/offline CI, a corporate proxy or firewall blocking unity3d.com, a transient DNS or connectivity blip, or a wrong/outdated version segment in the URL that still resolves to a host that drops the connection (note: a real 404 is an HTTPError and does NOT trigger this).","solutions":["Verify the host can reach docs.unity3d.com (curl -I https://docs.unity3d.com/ScriptReference/index.html) and configure HTTP(S)_PROXY if a proxy is required.","Retry the call once or twice — URLError is often transient (DNS hiccup, momentary reset).","Confirm DNS resolves docs.unity3d.com and that no firewall is dropping port 443.","If running fully offline, disable/skip docs tools rather than letting every lookup throw."],"exampleFix":"// before\nstatus, body = await _fetch_url(url)\n\n// after — tolerate transient reachability failures at the caller\ntry:\n    status, body = await _fetch_url(url)\nexcept ConnectionError as e:\n    return {\"status\": \"unavailable\", \"detail\": str(e)}","handlingStrategy":"retry","validationCode":"# Pre-flight reachability check before fetching docs\nimport socket\nfrom urllib.parse import urlparse\n\ndef can_reach(url: str, timeout: float = 3.0) -> bool:\n    host = urlparse(url).hostname\n    try:\n        socket.gethostbyname(host)\n        with socket.create_connection((host, 443), timeout=timeout):\n            return True\n    except OSError:\n        return False","typeGuard":"def is_connection_error(e: BaseException) -> bool:\n    return isinstance(e, ConnectionError) and 'Cannot reach' in str(e)","tryCatchPattern":"from services.tools.unity_docs import _fetch_url\n\nfor attempt in range(3):\n    try:\n        status, body = await _fetch_url(url)\n        break\n    except ConnectionError as e:\n        if attempt == 2:\n            return {\"status\": \"unavailable\", \"detail\": str(e)}\n        await asyncio.sleep(0.5 * (attempt + 1))","preventionTips":["Run docs tools in an environment with outbound HTTPS to docs.unity3d.com.","Configure HTTP_PROXY/HTTPS_PROXY in corporate networks before starting the server.","Cache doc lookups so transient blips don't repeat-fetch the same URL."],"tags":["network","docs","fetch","offline","urllib"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}