NousResearch/hermes-agent · critical · RuntimeError
Checksum mismatch for {asset_name}: expected {expected}, got
Error message
Checksum mismatch for {asset_name}: expected {expected}, got {actual} What it means
After downloading the iron-proxy release archive, its SHA-256 is compared against the entry in the release's checksums.txt (which is itself GPG-verified best-effort). A mismatch means the bytes on disk are not the bytes the release signed — either a corrupted/truncated download (proxies and flaky links are the usual cause) or genuine release-channel tampering. The installer refuses to extract and install the binary.
Source
Thrown at agent/proxy_sources/iron_proxy.py:505
checksum_path = tmp / _IRON_PROXY_CHECKSUM_NAME
logger.info("Downloading %s", asset_url)
_http_download(asset_url, archive_path)
_http_download(checksum_url, checksum_path)
# Defense-in-depth (maxpetrusenko P1): verify the GPG signature of
# checksums.txt before trusting it. The archive download honors ambient
# proxy env (urllib), so a compromised channel could serve a matching
# binary + checksums pair; the detached signature + pinned public key
# close that release-channel tamper gap. Best-effort: if gpg or the
# signature assets aren't available we log and fall back to the SHA-256
# check alone rather than hard-failing offline installs.
_verify_checksums_signature(tmp, checksum_path)
expected = _expected_sha256(checksum_path, asset_name)
actual = _sha256_file(archive_path)
if expected.lower() != actual.lower():
raise RuntimeError(
f"Checksum mismatch for {asset_name}: "
f"expected {expected}, got {actual}"
)
with tarfile.open(archive_path, "r:gz") as tf:
member = _pick_tar_member(tf, _platform_binary_name())
# PEP 706 data filter — strips ownership/mode replay (we set
# chmod explicitly below) AND rejects symlink/hardlink members
# that escape the extraction dir. Required on 3.12+ to silence
# the deprecation warning and on 3.14+ to opt into the
# tarbomb-rejecting default.
try:
tf.extract(member, tmp, filter="data") # noqa: S202
except TypeError:
# Python < 3.12 — filter kw didn't exist yet; the
# _pick_tar_member sanitization already rejects path
# traversal so this is acceptable.
tf.extract(member, tmp) # noqa: S202View on GitHub (pinned to c896c09c42)
Solutions
- Delete any partial download/temp state under the proxy state dir and retry `hermes egress install` on a stable connection.
- Check for intercepting proxies: verify `curl -L <asset-url> | sha256sum` matches the published checksums.txt entry from the same release page.
- If the mismatch reproduces on a clean network, treat it as a possible release-channel compromise — do NOT bypass the check; verify the checksums.txt GPG signature manually against the project's pinned key and report it.
Defensive patterns
Strategy: try-catch
Try / catch
try:
find_iron_proxy(install_if_missing=True)
except RuntimeError as e:
if "Checksum mismatch" in str(e):
# do NOT auto-retry in a loop: clear partial downloads once, retry once,
# then surface to a human — this can indicate tampering
raise Prevention
- Treat any reproducible checksum mismatch as a security incident, not a flake — verify the release manually before installing.
- Install from a trusted network without TLS-intercepting proxies.
- Pin the iron-proxy version and install once at image-build time instead of lazily at runtime.
When it happens
Trigger: find_iron_proxy(install_if_missing=True) / `hermes egress install` completes the archive download, but _sha256_file(archive_path) != _expected_sha256(checksum_path, asset_name). Typical causes: a corporate MITM proxy rewriting the GitHub asset download, a partially-written file from a dropped connection, or a partially-written file left in the temp dir.
Common situations: Installing behind an intercepting corporate proxy or on flaky Wi-Fi; a cached/partial download being reused; a genuinely compromised mirror (rare, but this check plus the GPG layer exists precisely for that).
Related errors
- Failed to download {url}: {exc}
- iron-proxy checksums.txt failed GPG signature verification —
- Refusing to start: could not pre-create audit log {audit_pat
- Refusing to write iron-proxy log {log_path}: {exc}. Remove
- ${label} exited and ${dashboardIndexUrl(baseUrl)} is served
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/99f794da59ef3ddc.
Report an issue: GitHub.