dotnet/aspnetcore · error · Exception

SHA256 mismatch for : expected , got

Error message

SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}

What it means

Raised by fetch_and_decompress when --force-check-gpg is set and the downloaded Packages.gz bytes hash to a different SHA256 than the value recorded in the Release file for that path. It guards the package index against mirror tampering or partial-publish drift, separate from the per-.deb check at error 345.

Solutions

  1. Switch --mirror to a known-consistent one (deb.debian.org or archive.ubuntu.com) and re-run.
  2. Drop corporate/transparent HTTP proxies that may serve stale cached bytes for Packages.gz while passing Release through fresh.
  3. Retry after a few minutes if the mirror is mid-publish; persistent mismatch indicates a real mirror bug worth reporting.
  4. Do not pass --force-check-gpg to silence it; that disables the entire chain of checks (348, 349, 350).

Example fix

# before
python3 install-debs.py --mirror http://local-mirror/debian --suite trixie --arch amd64 \
  --rootfsdir rootfs --force-check-gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg libc6
# 'SHA256 mismatch for main/binary-amd64/Packages.gz: expected ..., got ...'

# after (canonical mirror, no proxy)
unset http_proxy https_proxy
python3 install-debs.py --mirror http://deb.debian.org/debian --suite trixie --arch amd64 \
  --rootfsdir rootfs --force-check-gpg --keyring /usr/share/keyrings/debian-archive-keyring.gpg libc6
Defensive patterns

Strategy: validation

Validate before calling

# Compare the Release-file SHA256 for Packages.gz against the actual file before the run:
rel=$(curl -fsS "$MIRROR/dists/$SUITE/Release")
exp=$(printf '%s' "$rel" | awk -v p="main/binary-$ARCH/Packages.gz" '$1 ~ /^[0-9a-f]{64}$/ && $3==p {print $1}')
got=$(curl -fsS "$MIRROR/dists/$SUITE/main/binary-$ARCH/Packages.gz" | sha256sum | cut -d' ' -f1)
[ "$exp" = "$got" ] || { echo 'index/release drift; switch mirror'; exit 1; }

Try / catch

try:
    asyncio.run(download_package_index_parallel(mirror, arch, suites, True, keyring))
except Exception as e:
    if 'SHA256 mismatch' in str(e) and 'Packages.gz' in str(e):
        print('Release/Packages.gz drift from mirror; switch --mirror.')
        raise

Prevention

When it happens

Trigger: fetch_and_decompress computes hashlib.sha256(compressed_data) on the raw gzip bytes of Packages.gz and compares against packages_sha returned by parse_release_file(release_file_content, path). Any mismatch raises this Exception before the index is parsed.

Common situations: Mirror is mid-publish: Release file was updated but Packages.gz still serves the previous bytes (or vice versa); mirror snapshotted at an inconsistent point; transparent proxy cached an older Packages.gz against a newer Release; suite was renamed or pointed at a wrong components path; an attacker altered the index (rare; the gpg check at 349 would usually catch the Release tampering first).

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/53239fb65f1d09d6. Report an issue: GitHub.

Appendix: source

Thrown at eng/common/cross/install-debs.py:105

    """Fetch and decompress the Packages.gz file."""

    path = f"{component}/binary-{arch}/Packages.gz"
    url = f"{mirror}/dists/{suite}/{path}"

    async with session.get(url) as response:
        if response.status == 200:
            compressed_data = await response.read()
            decompressed_data = gzip.decompress(compressed_data).decode('utf-8')
            print(f"Downloaded index: {url}")

            if check_sig:
                # Verify the package index against the sha256 recorded in the Release file
                release_file_content = await fetch_release_file(session, mirror, suite, keyring)
                packages_sha = parse_release_file(release_file_content, path)

                sha256 = hashlib.sha256(compressed_data).hexdigest()
                if sha256 != packages_sha:
                    raise Exception(f"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}")
                print(f"Checksum verified for {path}")

            return decompressed_data
        else:
            print(f"Skipped index: {url} (doesn't exist)")
            return None

async def fetch_release_file(session, mirror, suite, keyring):
    """Fetch Release and Release.gpg files and verify the signature."""

    release_url = f"{mirror}/dists/{suite}/Release"
    release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"

    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
        await download_file(session, release_url, release_file.name)
        await download_file(session, release_gpg_url, release_gpg_file.name)

        print("Verifying signature of Release with Release.gpg.")

View on GitHub (pinned to 3600ca084e)