puppetlabs/puppet · error · ValidationFailure
#{version} is not a valid python package version. Please ref
Error message
#{version} is not a valid python package version. Please refer to https://www.python.org/dev/peps/pep-0440/. What it means
First guard in Puppet::Util::Package::Version::Pip.parse (pip.rb:39): raises ValidationFailure (interpolating version.to_s) when the argument is not a String. The class only accepts raw PEP 440 version strings; it does not coerce nil, Symbol, Float, or Integer input.
Source
Thrown at lib/puppet/util/package/version/pip.rb:39
(?:
[-_\\.]?
(?<post_l>post|rev|r)
[-_\\.]?
(?<post_n2>[0-9]+)?
)
)?
(?<dev> # dev release
[-_\\.]?
(?<dev_l>dev)
[-_\\.]?
(?<dev_n>[0-9]+)?
)?
)
(?:\\+(?<local>[a-z0-9]+(?:[-_\\.][a-z0-9]+)*))? # local version
"
def self.parse(version)
raise ValidationFailure, version.to_s unless version.is_a? String
matched = version.match(Regexp.new("^\\s*" + VERSION_PATTERN + "\\s*$", Regexp::EXTENDED | Regexp::MULTILINE | Regexp::IGNORECASE))
raise ValidationFailure, version unless matched
new(matched)
end
def self.compare(version_a, version_b)
version_a = parse(version_a) unless version_a.is_a?(self)
version_b = parse(version_b) unless version_b.is_a?(self)
version_a <=> version_b
end
def to_s
parts = []
parts.push("#{@epoch_data}!") if @epoch_data && @epoch_data != 0View on GitHub (pinned to e227c27540)
Solutions
- Convert to String first: Pip.parse(version.to_s) when the source type is not guaranteed.
- Type-check the input at the boundary: only call parse for String values.
- Rescue Puppet::Util::Package::Version::Pip::ValidationFailure around third-party input.
Example fix
// before v = Puppet::Util::Package::Version::Pip.parse(cfg[:version]) # cfg[:version] = 2.8 (Float) // after raw = cfg[:version] raw = raw.to_s if raw.is_a?(Numeric) || raw.is_a?(Symbol) v = Puppet::Util::Package::Version::Pip.parse(raw) if raw.is_a?(String)
Defensive patterns
Strategy: type-guard
Validate before calling
def pip_parseable?(value) value.is_a?(String) && !value.strip.empty? end
Type guard
def pip_version_input?(v) v.is_a?(String) || v.is_a?(Numeric) || v.is_a?(Symbol) # caller must to_s the latter two end
Try / catch
begin Puppet::Util::Package::Version::Pip.parse(value.to_s) rescue Puppet::Util::Package::Version::Pip::ValidationFailure nil end
Prevention
- Coerce numerics/symbols from JSON/YAML with to_s before pip version handling.
- Guard nil at the source: check the config key exists before using its value as a version.
- Keep one normalization choke-point for version input instead of parsing at multiple call sites.
When it happens
Trigger: Pip.parse(nil), Pip.parse(:'1.0.0'), Pip.parse(1.0) (a Float that should have been '1.0'), Pip.parse(3) (e.g. Python major version kept as Integer in config data).
Common situations: Version data loaded from JSON/YAML/Hiera where '2.0' became 2.0; nil defaults leaking when a config key is missing; code shared between gem and pip handling that passes through typed values unchanged.
Related errors
- #{version} is not a valid ruby gem version.
- Cannot compare #{this} (Array) with #{other} (#{other.class}
- Cannot compare #{this} (#{this.class}) with #{other} (Array)
- Unable to parse '#{range_string}' as a string
- field 'data_provider' must be a string
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/dc66e9eef876de0f.
Report an issue: GitHub.