puppetlabs/puppet · error · Puppet::Error

Cannot compare #{this} (#{this.class}) with #{other} (Array)

Error message

Cannot compare #{this} (#{this.class}) with #{other} (Array). Only ±Float::INFINITY accepted.

What it means

Mirror of the previous guard in Pip.compare (pip.rb:160): the LEFT comparison key is a non-Array scalar and the RIGHT is an Array. The scalar must be +/- Float::INFINITY (the sentinel used by #key for missing segments); any finite value raises Puppet::Error 'Only ±Float::INFINITY accepted.' before the comparison can proceed.

Source

Thrown at lib/puppet/util/package/version/pip.rb:160

      end

      [epoch, release_key, pre_key, post_key, dev_key, local_key]
    end

    def compare(this, other)
      if (this.is_a? Array) && (other.is_a? Array)
        this  << -Float::INFINITY if this.length < other.length
        other << -Float::INFINITY if this.length > other.length

        this.each_with_index do |element, index|
          return compare(element, other.at(index)) if element != other.at(index)
        end
      elsif (this.is_a? Array) && !(other.is_a? Array)
        raise Puppet::Error, "Cannot compare #{this} (Array) with #{other} (#{other.class}). Only ±Float::INFINITY accepted." unless other.abs == Float::INFINITY

        return other == -Float::INFINITY ? 1 : -1
      elsif !(this.is_a? Array) && (other.is_a? Array)
        raise Puppet::Error, "Cannot compare #{this} (#{this.class}) with #{other} (Array). Only ±Float::INFINITY accepted." unless this.abs == Float::INFINITY

        return this == -Float::INFINITY ? -1 : 1
      end
      this <=> other
    end

    class ValidationFailure < ArgumentError
      def initialize(version)
        super("#{version} is not a valid python package version. Please refer to https://www.python.org/dev/peps/pep-0440/.")
      end
    end
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. Rebuild both operands via Pip.parse immediately before comparing; never compare cached/stubbed key data.
  2. Freeze/re-parse cache entries when the library version changes; include the puppet version in the cache key.
  3. In specs, use parsed instances instead of objects with fabricated instance variables.
  4. Rescue Puppet::Error at the comparison boundary and re-parse once as a recovery path.

Example fix

// before
sorted = cached_versions.sort # one entry has a stale scalar key element

// after
sorted = cached_versions.map { |v| Puppet::Util::Package::Version::Pip.parse(v.to_s) }.sort
Defensive patterns

Strategy: type-guard

Type guard

PIP = Puppet::Util::Package::Version::Pip
def pip_instances?(*versions)
  versions.all? { |v| v.is_a?(PIP) }
end

Try / catch

begin
  sorted = versions.sort
rescue Puppet::Error => e
  raise unless e.message.include?('Only ±Float::INFINITY')
  versions.map! { |v| PIP.parse(v.to_s) }
  retry
end

Prevention

When it happens

Trigger: Same shape as 1006 with sides swapped: comparing a hand-built/stale key whose slot holds a finite Integer/Float (e.g., an epoch-style number left in the release position) against a properly parsed version whose release/local key is an Array; a stubbed peer object returning a scalar from #key where the real class returns an Array.

Common situations: Stale serialized version objects compared after a puppet upgrade changed #key's shape; test fixtures constructing key arrays by hand; caches (memoization) shared across versions of the class.

Related errors


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