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

Operator '#{operator}' is not implemented

Error message

Operator '#{operator}' is not implemented

What it means

The operator dispatch in Range.parse (range.rb:51): FULL_REGEX happily captures ANY run of [<>=] characters as the 'operator', but the case statement only implements '>', '>=', '<', '<=' and '' (empty = exact equality). Any other captured operator falls to else and raises ValidationFailure 'Operator ... is not implemented'.

Source

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

      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"
        end
      end
      simples.size == 1 ? simples[0] : MinMax.new(simples[0], simples[1])
    end
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. For exact pins, drop the operator entirely: Range.parse('1.2.3', klass) maps to Eq.
  2. Translate equality syntax before parsing: replace '==' or '=' at token start with ''.
  3. Whitelist-validate tokens: /\A(?:>=|<=|>|<)?[^<>=]+\Z/ before handing to parse.
  4. Rescue Range::ValidationFailure and surface a message listing supported operators > >= < <= and bare version.

Example fix

// before
rng = Puppet::Util::Package::Version::Range.parse(ensure_spec, klass) # ensure_spec = '==1.26.0'

// after
spec = ensure_spec.strip.gsub(/\A==?/, '')
rng = Puppet::Util::Package::Version::Range.parse(spec, klass) # '1.26.0' -> Eq
Defensive patterns

Strategy: validation

Validate before calling

SUPPORTED = /\A(?:>=|<=|>|<)?(?!=)[^<>=]+\z/
return :invalid unless constraint.match?(SUPPORTED)

Try / catch

begin
  Range.parse(constraint, version_class)
rescue Puppet::Util::Package::Version::Range::ValidationFailure => e
  raise e if e.message.include?('not implemented')
  raise
end

Prevention

When it happens

Trigger: Range.parse('==1.0', klass) (pip/gem equality syntax -> operator '=='), Range.parse('=1.0', klass) (single '=' is a captured operator, NOT the empty/Equality case -> raise), Range.parse('=>1.0', klass), Range.parse('<>1.0', klass), Range.parse('>>1', klass).

Common situations: Constraints copied from requirements.txt ('==1.26.0') or Gemfile ('= 1.0'); npm-style '^1.2.3' would instead fail the FULL_REGEX match (caret is captured by (.+) as part of the version and then fails version_class.parse); users assuming '=' means pinned.

Related errors


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