{"record":{"id":"20de38459febe838","repo":"NousResearch/hermes-agent","slug":"failed-to-download-url-exc","errorCode":null,"errorMessage":"Failed to download {url}: {exc}","messagePattern":"Failed to download (.+?): (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/proxy_sources/iron_proxy.py","lineNumber":556,"sourceCode":"\n    # Invalidate the version cache so a freshly-installed binary\n    # re-probes ``--version`` on the next ``get_status()`` call instead\n    # of returning the pre-upgrade string.  Long-lived processes that\n    # bump the pinned version via ``force=True`` need this.\n    _VERSION_CACHE.pop(str(target), None)\n\n    logger.info(\"Installed iron-proxy %s at %s\", _IRON_PROXY_VERSION, target)\n    return target\n\n\ndef _http_download(url: str, dest: Path) -> None:\n    req = urllib.request.Request(url, headers={\"User-Agent\": \"hermes-agent\"})\n    try:\n        with urllib.request.urlopen(req, timeout=_DOWNLOAD_TIMEOUT) as resp:  # noqa: S310\n            with open(dest, \"wb\") as f:\n                shutil.copyfileobj(resp, f)\n    except urllib.error.URLError as exc:\n        raise RuntimeError(f\"Failed to download {url}: {exc}\") from exc\n\n\ndef _verify_checksums_signature(tmp: Path, checksum_path: Path) -> bool:\n    \"\"\"Best-effort GPG verification of ``checksums.txt`` (maxpetrusenko P1).\n\n    Downloads the detached signature (``checksums.txt.asc``) and the release\n    signing key (``public-key.asc``), imports the key into an ephemeral\n    keyring, and verifies the signature over ``checksum_path``.\n\n    Returns True when the signature is verified. Returns False (with a warning)\n    when verification is unavailable — ``gpg`` not installed, or the signature /\n    public-key assets are missing from the release. Raises RuntimeError ONLY\n    when verification actively FAILS (a present-but-bad signature), which is a\n    tamper signal we must not ignore.\n\n    Rationale for graceful degradation on \"unavailable\": the SHA-256 check\n    against ``checksums.txt`` remains in force regardless, and many install\n    hosts (CI, minimal containers) won't have gpg. We harden when we can and","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/proxy_sources/iron_proxy.py#L538-L574","documentation":"The release asset download uses urllib with a plain Request; any urllib.error.URLError (DNS failure, connection refused, TLS error, HTTP 404 surfaced as URLError) is wrapped into this RuntimeError with the failing URL. It fires during the lazy auto-install path, before any checksum or extraction work.","triggerScenarios":"find_iron_proxy(install_if_missing=True) / `hermes egress install` when the machine is offline, DNS for the release host fails, a firewall blocks egress, the tagged release/asset was removed upstream, or ambient HTTP(S)_PROXY env points at a dead proxy (the download honors ambient proxy env).","commonSituations":"Egress-filtered containers and CI sandboxes; stale _IRON_PROXY_VERSION pin pointing at a release whose assets were re-uploaded/renamed; broken proxy env inherited from the operator's shell.","solutions":["Confirm network reachability of the URL in the message (curl -I) and fix DNS/firewall/proxy env.","If the release tag/asset 404s, update to a Hermes version whose _IRON_PROXY_VERSION matches an existing release, or manually install the binary.","Pre-install the binary manually (or with `hermes egress install` on a connected machine) so runtime lazy-install is never triggered."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import socket, urllib.request\n\ndef release_host_reachable(url: str) -> bool:\n    try:\n        urllib.request.urlopen(urllib.request.Request(url, method=\"HEAD\"), timeout=10)\n        return True\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":"for attempt in range(2):\n    try:\n        find_iron_proxy(install_if_missing=True)\n        break\n    except RuntimeError as e:\n        if \"Failed to download\" in str(e) and attempt == 0:\n            continue  # one retry for transient network\n        raise","preventionTips":["Run `hermes egress install` at provisioning time on a connected host rather than lazily at first use.","Keep HTTP(S)_PROXY env pointing at a live proxy or unset it.","Alert on asset-404 URLErrors — they usually mean the version pin drifted from the release catalog."],"tags":["network","download","install","iron-proxy"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}