rust-lang/rust · critical · RuntimeError

hash mismatch for package {path}: {sha1} vs {sha1_known} (kn

Error message

hash mismatch for package {path}: {sha1} vs {sha1_known} (known good)

What it means

Raised by Package.download() in android-sdk-manager.py at line 56 when the SHA-1 hash of the downloaded file does not match the expected (pinned) sha1 stored in the Package object. This is a tamper/integrity check for Android SDK packages. The lockfile pins each package's URL and sha1; if the downloaded content differs, the script aborts to prevent using a corrupted or compromised package.

Source

Thrown at src/ci/docker/scripts/android-sdk-manager.py:56

class Package:
    def __init__(self, path, url, sha1, deps=None):
        if deps is None:
            deps = []
        self.path = path.strip()
        self.url = url.strip()
        self.sha1 = sha1.strip()
        self.deps = deps

    def download(self, base_url):
        _, file = tempfile.mkstemp()
        url = base_url + self.url
        subprocess.run(["curl", "-o", file, url], check=True)
        # Ensure there are no hash mismatches
        with open(file, "rb") as f:
            sha1 = hashlib.sha1(f.read()).hexdigest()
            if sha1 != self.sha1:
                raise RuntimeError(
                    "hash mismatch for package "
                    + self.path
                    + ": "
                    + sha1
                    + " vs "
                    + self.sha1
                    + " (known good)"
                )
        return file

    def __repr__(self):
        return "<Package " + self.path + " at " + self.url + " (sha1=" + self.sha1 + ")"


def fetch_url(url):
    page = urllib.request.urlopen(url)
    return page.read()

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Re-download the package (delete the temp file / cached copy and retry).
  2. If the mirror is stale or corrupted, update the lockfile by re-running 'android-sdk-manager.py add-to-lockfile' with the package name to get the current sha1 from Google's repository, then 'update-mirror' to push a fresh copy.
  3. Verify network/proxy integrity (run curl manually and compare sha1).
  4. If the Google-side package legitimately changed, update the lockfile sha1 to match the new known-good hash after verifying the source.

Example fix

# before: lockfile pins stale sha1
# packages-lock.txt
sdk-tools-linux;4333796.zip <url> <old-sha1>
# after: regenerate lockfile
python3 android-sdk-manager.py add-to-lockfile packages-lock.txt 'sdk-tools-linux;4333796'
Defensive patterns

Strategy: validation

Validate before calling

# Before relying on a downloaded package, verify its hash
import hashlib
def verify_sha1(filepath, expected_sha1):
    with open(filepath, 'rb') as f:
        actual = hashlib.sha1(f.read()).hexdigest()
    if actual != expected_sha1:
        print(f'Hash mismatch: {actual} vs expected {expected_sha1}')
        return False
    return True

Try / catch

try:
    path = package.download(base_url)
except RuntimeError as e:
    if 'hash mismatch' in str(e):
        # Package may be corrupted on the mirror
        print(f'Hash mismatch for {package.path}. Consider updating the lockfile.')
        print('Run: android-sdk-manager.py add-to-lockfile <lockfile> <package>')
    raise

Prevention

When it happens

Trigger: Package.download(base_url) fetches the file via curl at line 51, reads it, computes hashlib.sha1 at line 54, and compares at line 55. If sha1 != self.sha1, RuntimeError is raised at line 56. The expected sha1 comes from the lockfile (path/url/sha1 per line) or from the Google repository XML.

Common situations: A mirrored package on the rust-lang-ci-mirrors S3 bucket was corrupted or replaced; the Google repository updated a package but the lockfile still pins the old sha1; a network/proxy issue corrupted the download; or the mirror bucket content is stale.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/a896816735f1f4fd. Report an issue: GitHub.