dotnet/aspnetcore · critical · Exception

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

Error message

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

What it means

When --force-check-gpg is set, fetch_and_decompress verifies each Packages.gz against the SHA256 recorded in the Release file before decompressing it. A mismatch raises a plain Exception. This catches a tampered or stale package index whose checksum does not match the signed Release — a stronger integrity guarantee than the per-deb check because it is anchored to a GPG-signed file.

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 294cab2f9b)

Solutions

  1. Use a mirror known to be fully synced (deb.debian.org, snapshot.debian.org pinned to a timestamp, or a local complete mirror).
  2. Wait for the mirror sync window to pass and re-run — Release/Packages skew is transient.
  3. If you trust the transport, you may drop --force-check-gpg to skip this check, but prefer fixing the mirror.
  4. Pin to a snapshot mirror (e.g., snapshot.debian.org/archive/debian/<timestamp>/) so Release and Packages are atomically consistent.

Example fix

# before — rolling mirror with Release/Packages skew
python3 install-debs.py --force-check-gpg --keyring key.gpg \
  --suite sid --mirror http://deb.debian.org/debian ...
# SHA256 mismatch for main/binary-amd64/Packages.gz

# after — pinned snapshot
python3 install-debs.py --force-check-gpg --keyring key.gpg \
  --suite sid --mirror http://snapshot.debian.org/archive/debian/20240101T000000Z/ ...
Defensive patterns

Strategy: fallback

Validate before calling

# Pre-validate Release/Packages consistency before extracting
async with session.get(release_url) as r:
    release_text = await r.text()
packages_sha = parse_release_file(release_text, path)
# compare against the live Packages.gz hash; abort early if mismatch
if packages_sha is None:
    print(f'No checksum recorded for {path} in Release; mirror layout mismatch')

Try / catch

try:
    content = await fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring)
except Exception as e:
    if 'SHA256 mismatch for' in str(e) and 'Packages' in str(e):
        # Release/Packages skew — fall back to a snapshot mirror
        snapshot_mirror = f'http://snapshot.debian.org/archive/debian/{pin}/'
        content = await fetch_and_decompress(session, snapshot_mirror, arch, suite, component, check_sig, keyring)
    raise

Prevention

When it happens

Trigger: fetch_and_decompress fetches the Packages.gz for a suite/component, then if check_sig is True it fetches the Release file via gpgv-verified download, parses it for the sha256 of the relative path (e.g., main/binary-amd64/Packages.gz), and compares. A mismatch raises immediately.

Common situations: Mirror mid-sync (Release updated before Packages.gz or vice versa); partial mirror that ships Release but not the matching Packages; CDN serving a cached Release with fresh Packages; man-in-the-middle tampering.

Related errors


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