puppetlabs/puppet · error · RuntimeError
Downloaded release for %{name} did not match expected checks
Error message
Downloaded release for %{name} did not match expected checksum %{checksum} What it means
Puppet raises this RuntimeError in Puppet::Forge::Release#validate_checksum after a module release tarball is downloaded from the Forge. The hex digest computed over the downloaded file does not equal the checksum string that the Forge metadata published for that release. It means the bytes you received are not the bytes the Forge published: corruption in transit, a truncating proxy or cache, or a tampered artifact.
Source
Thrown at lib/puppet/forge.rb:221
@file ||= Tempfile.new(name, Puppet::Forge::Cache.base_path).tap(&:binmode)
end
# rubocop:enable Naming/MemoizedInstanceVariableName
def download(uri, destination)
response = @source.make_http_request(uri, destination)
destination.flush and destination.close
unless response.code == 200
raise Puppet::Forge::Errors::ResponseError.new(:uri => response.url, :response => response)
end
end
def validate_checksum(file, checksum, digest_class)
if Puppet.runtime[:facter].value(:fips_enabled) && digest_class == Digest::MD5
raise _("Module install using MD5 is prohibited in FIPS mode.")
end
if digest_class.file(file.path).hexdigest != checksum
raise RuntimeError, _("Downloaded release for %{name} did not match expected checksum %{checksum}") % { name: name, checksum: checksum }
end
end
def unpack(file, destination)
Puppet::ModuleTool::Applications::Unpacker.unpack(file.path, destination)
rescue Puppet::ExecutionFailure => e
raise RuntimeError, _("Could not extract contents of module archive: %{message}") % { message: e.message }
end
def deprecated?
@data['module'] && !@data['module']['deprecated_at'].nil?
end
end
private
def process(list)
l = list.map do |release|View on GitHub (pinned to e227c27540)
Solutions
- Retry the install. Transient corruption is the most common cause, and a retry fetches a fresh tarball.
- Download the tarball by hand (curl -sSL <release_url> | sha256sum) and compare with the checksum on the Forge release page. If they differ, the corruption is between you and the Forge: clear proxy and mirror caches, then retry.
- Check free space on the temp filesystem (df -h /tmp and puppet config print module_working_dir). ENOSPC during destination.flush/close produces a truncated file and this exact mismatch.
- If the manual download matches the Forge checksum but the install still fails, purge Puppet's own module cache directory and retry.
- On very old Puppet versions in FIPS mode, note that the MD5 path is rejected outright; upgrade Puppet so the release uses SHA-256.
Example fix
# before: corrupted cached tarball keeps failing every run puppet module install puppetlabs-stdlib --version 9.0.0 # -> Downloaded release for puppetlabs-stdlib did not match expected checksum ... # after: purge the partial download so the tarball is fetched fresh, then retry rm -rf "$(puppet config print module_working_dir)"/* puppet module install puppetlabs-stdlib --version 9.0.0
Defensive patterns
Strategy: retry
Validate before calling
# Before programmatic installs: give the download a clean, roomy target require 'fileutils' FileUtils.rm_rf(Puppet[:module_working_dir]) if Puppet[:module_working_dir] abort 'not enough disk for module install' if `df -Pk /tmp`.split[3].to_i < 50_000
Try / catch
attempts = 0
begin
Puppet::ModuleTool::Applications::Installer.run('puppetlabs-stdlib', modulepath: mp)
rescue RuntimeError => e
raise unless e.message =~ /did not match expected checksum/
FileUtils.rm_rf(Puppet[:module_working_dir]) # purge the corrupted tarball
retry if (attempts += 1) < 3
raise
end Prevention
- Purge the module working dir before scripted installs so partial tarballs never persist.
- Bypass or fix SSL-inspecting proxies for forgeapi.puppet.com downloads.
- Keep mirrors (Artifactory/Nexus) healthy and pinned to verified checksums.
- Pin module versions in a Puppetfile so re-downloads are rare and reproducible.
When it happens
Trigger: A 'puppet module install' (or Puppet::Forge API use: release.download then validate_checksum) where digest_class.file(file.path).hexdigest != checksum. Typical producers: an SSL-inspecting proxy returns a truncated body or an HTML error page as the tarball; a mirror or Artifactory cache serves mixed bytes; the temp file write fails part way (disk full) so the digest runs over a partial file.
Common situations: Corporate proxies and TLS-inspection appliances that alter download bodies; stale or corrupted artifacts in a module mirror; a full /tmp or module_working_dir that truncates the write; flaky networks that drop the body mid-stream without closing with an error.
Related errors
- Could not extract contents of module archive: %{message}
- Could not %{action} '%{module_name}', did you mean '%{sugges
- File not found
- Malformed dependency: %{name}. Exception was: %{detail}
- Existing backup and new file have different content but same
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/4b60008371a6238d.
Report an issue: GitHub.