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

  1. Retry the install. Transient corruption is the most common cause, and a retry fetches a fresh tarball.
  2. 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.
  3. 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.
  4. If the manual download matches the Forge checksum but the install still fails, purge Puppet's own module cache directory and retry.
  5. 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

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


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/4b60008371a6238d. Report an issue: GitHub.