puppetlabs/puppet · error · Puppet::Util::Package::Version::Range::ValidationFailure

Unable to parse '#{range_string}' as a string

Error message

Unable to parse '#{range_string}' as a string

What it means

Puppet::Util::Package::Version::Range.parse (range.rb:33) raises ValidationFailure unless range_string.is_a?(String). The parser then splits on whitespace and interprets each token as an optional operator (> >= < <=) plus a version, delegating version parsing to the supplied version_class (Debian/Gem/Pip).

Source

Thrown at lib/puppet/util/package/version/range.rb:33

    # Currently parsed version range string may take any of the following
    # forms:
    #
    # * Regular Version strings
    #   * ex. `"1.0.0"`, `"1.2.3-pre"`
    # * Inequalities
    #   * ex. `">1.0.0"`, `"<3.2.0"`, `">=4.0.0"`
    # * Range Intersections (min is always first)
    #   * ex. `">1.0.0 <=2.3.0"`
    #
    RANGE_SPLIT = /\s+/
    FULL_REGEX = /\A((?:[<>=])*)(.+)\Z/

    # @param range_string [String] the version range string to parse
    # @param version_class [Version] a version class implementing comparison operators and parse method
    # @return [Range] a new {Range} instance
    # @api public
    def self.parse(range_string, version_class)
      raise ValidationFailure, "Unable to parse '#{range_string}' as a string" unless range_string.is_a?(String)

      simples = range_string.split(RANGE_SPLIT).map do |simple|
        match, operator, version = *simple.match(FULL_REGEX)
        raise ValidationFailure, "Unable to parse '#{simple}' as a version range identifier" unless match

        case operator
        when '>'
          Gt.new(version_class.parse(version))
        when '>='
          GtEq.new(version_class.parse(version))
        when '<'
          Lt.new(version_class.parse(version))
        when '<='
          LtEq.new(version_class.parse(version))
        when ''
          Eq.new(version_class.parse(version))
        else
          raise ValidationFailure, "Operator '#{operator}' is not implemented"

View on GitHub (pinned to e227c27540)

Solutions

  1. Join token arrays first: Range.parse(tokens.join(' '), version_class).
  2. Check for nil/blank input before calling: return a default (e.g., Eq of the current version) when unset.
  3. Type-guard with is_a?(String) and raise a descriptive error of your own naming the parameter.
  4. Rescue Range::ValidationFailure (ArgumentError subclass) at input boundaries.

Example fix

// before
rng = Puppet::Util::Package::Version::Range.parse(params[:version_range], Debian) # nil

// after
raw = params[:version_range]
rng = if raw.is_a?(String) && !raw.strip.empty?
        Puppet::Util::Package::Version::Range.parse(raw, Debian)
      else
        Puppet::Util::Package::Version::Range::Eq.new(Debian.parse(default_version))
      end
Defensive patterns

Strategy: type-guard

Validate before calling

def range_string?(v)
  v.is_a?(String) && !v.strip.empty?
end

Type guard

def range_input?(v)
  v.is_a?(String)
end

Try / catch

begin
  Puppet::Util::Package::Version::Range.parse(str, version_class)
rescue Puppet::Util::Package::Version::Range::ValidationFailure
  nil
end

Prevention

When it happens

Trigger: Range.parse(nil, Debian), Range.parse(['>1.0'], Gem) (array of tokens instead of joined string), Range.parse(range_object, version_class) (passing an already-parsed Gt/Eq instance back in), Range.parse(:'>=1.0', Pip) (symbol).

Common situations: nil default when a manifest parameter is unset; arrays from data bindings passed unjoined; round-tripping parsed ranges through code that expects raw strings.

Understand the failure class

Related errors


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