puppetlabs/puppet · error · Puppet::Module::Task::InvalidMetadata

puppet.tasks/unparseable-metadata

puppet.tasks/unparseable-metadata

Error message

err.message

What it means

Raised as Puppet::Util::Package::Version::Debian::ValidationFailure (an ArgumentError) by Debian.parse when the argument is not a String — the very first check in parse, before the epoch/upstream/revision regex is applied. nil, symbols, integers, or floats all fail here; a String that merely fails the version grammar raises the sibling error 'as a debian version identifier' instead. The returned instance is frozen and built from epoch.to_i plus the captured groups.

Source

Thrown at lib/puppet/module/task.rb:250

      name = task_name == "init" ? pup_module.name : "#{pup_module.name}::#{task_name}"

      @module = pup_module
      @name = name
      @metadata_file = metadata_file
      @module_executables = module_executables || []
    end

    def self.read_metadata(file)
      if file
        content = Puppet::FileSystem.read(file, :encoding => 'utf-8')
        content.empty? ? {} : Puppet::Util::Json.load(content)
      end
    rescue SystemCallError, IOError => err
      msg = _("Error reading metadata: %{message}" % { message: err.message })
      raise InvalidMetadata.new(msg, 'puppet.tasks/unreadable-metadata')
    rescue Puppet::Util::Json::ParseError => err
      raise InvalidMetadata.new(err.message, 'puppet.tasks/unparseable-metadata')
    end

    def metadata
      @metadata ||= self.class.read_metadata(@metadata_file)
    end

    def files
      @files ||= self.class.find_files(@name, @module.tasks_directory, metadata, @module_executables, environment_name)
    end

    def validate
      files
      true
    end

    def ==(other)
      name == other.name &&
        self.module == other.module

View on GitHub (pinned to e227c27540)

Solutions

  1. Coerce before parsing: pass `ver.to_s` (and skip the call when ver.nil?) so parse always receives a String
  2. Default optional Hiera keys to a real version string or handle nil explicitly at the call site
  3. If you meant a non-Debian scheme, use the matching class (Version::Rpm, Version::Gem, Version::Pip) — they all have the same String contract

Example fix

# before
ver = Puppet::Util::Package::Version::Debian.parse(params['version']) # nil -> ValidationFailure

# after
raw = params['version']
ver = raw.nil? ? nil : Puppet::Util::Package::Version::Debian.parse(raw.to_s)
Defensive patterns

Strategy: type-guard

Type guard

def debian_version_string?(ver)
  ver.is_a?(String) && !ver.empty? && ver.match?(Puppet::Util::Package::Version::Debian::REGEX_FULL_RX)
end

Puppet::Util::Package::Version::Debian.parse(v) if debian_version_string?(v)

Try / catch

begin
  Puppet::Util::Package::Version::Debian.parse(ver)
rescue Puppet::Util::Package::Version::Debian::ValidationFailure => e
  raise unless e.message =~ /as a string/
  Puppet::Util::Package::Version::Debian.parse(ver.to_s)
end

Prevention

When it happens

Trigger: Calling Puppet::Util::Package::Version::Debian.parse(nil), parse(:present), or parse(1.9) — typically because a package ensure/Version value arrived untyped from Hiera, an ENC, or user input and was passed through without coercion. Also parse(v) where v is a Puppet::Parser::AST wrapper or params class instead of a plain string.

Common situations: Hiera lookups returning nil for an optional version key that is forwarded anyway; package providers receiving `ensure => latest` symbols into version comparison paths; custom types passing numeric versions (1.9) where the Debian comparison class expects '1.9'.

Related errors


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