puppetlabs/puppet · error · ArgumentError
'from' must be less or equal to 'to'. Got (#{@from}, #{@to}
Error message
'from' must be less or equal to 'to'. Got (#{@from}, #{@to} What it means
PTimespanType and PTimestampType (both PAbstractTimeDataType) are ranged types: initialize converts both bounds via convert_arg (nil/:default become -/+Infinity, Strings are parsed, numbers are seconds) and then requires from <= to. A reversed or overlapping-wrong range raises ArgumentError "'from' must be less or equal to 'to'" (the message text is missing its closing parenthesis - cosmetic bug in the source).
Source
Thrown at lib/puppet/pops/types/p_timespan_type.rb:11
# frozen_string_literal: true
module Puppet::Pops
module Types
class PAbstractTimeDataType < PScalarType
# @param from [AbstractTime] lower bound for this type. Nil or :default means unbounded
# @param to [AbstractTime] upper bound for this type. Nil or :default means unbounded
def initialize(from, to = nil)
@from = convert_arg(from, true)
@to = convert_arg(to, false)
raise ArgumentError, "'from' must be less or equal to 'to'. Got (#{@from}, #{@to}" unless @from <= @to
end
# Checks if this numeric range intersects with another
#
# @param o [PNumericType] the range to compare with
# @return [Boolean] `true` if this range intersects with the other range
# @api public
def intersect?(o)
instance_of?(o.class) && !(@to < o.numeric_from || o.numeric_to < @from)
end
# Returns the lower bound of the numeric range or `nil` if no lower bound is set.
# @return [Float,Integer]
def from
@from == -Float::INFINITY ? nil : @from
end
# Returns the upper bound of the numeric range or `nil` if no upper bound is set.View on GitHub (pinned to e227c27540)
Solutions
- Swap the arguments so the earlier/lower value comes first: Timespan[60, 120]
- When bounds come from variables, normalize with min/max (or [a, b].minmax) before constructing the type
- Leave a bound as default/unbounded rather than passing an inverted sentinel value
Example fix
# before $window = Timespan[120, 60] # from > to $range = Timestamp['2024-06-01T00:00:00 UTC', '2024-01-01T00:00:00 UTC'] # after $window = Timespan[60, 120] $from, $to = [$start, $end].minmax # normalize variable bounds $range = Timestamp[$from, $to]
Defensive patterns
Strategy: validation
Validate before calling
# normalize bounds before constructing the ranged type from, to = [from_bound, to_bound].minmax # after converting to comparable Timespan/Timestamp raise 'inverted range' if from && to && from > to Timespan.new(from, to) # or Timespan[from, to] in DSL
Type guard
def valid_time_bounds?(from, to) f = from.nil? || from == :default ? -Float::INFINITY : from.to_f t = to.nil? || to == :default ? Float::INFINITY : to.to_f f <= t end
Try / catch
begin Timespan[from_arg, to_arg] rescue ArgumentError => e raise unless e.message =~ /'from' must be less or equal to 'to'/ Timespan[to_arg, from_arg] # deliberate swap only when mis-ordering is provably the cause end
Prevention
- Compute window bounds with min/max before building types from user data
- Watch timezone/DST handling when parsing timestamp bounds from strings
- Leave a bound unset (default) instead of passing sentinel values
When it happens
Trigger: Timespan[120, 60] (lower bound of 120 seconds above upper bound of 60), or Timestamp['2024-01-01T00:00:00 UTC', '2023-12-31T23:59:59 UTC']. Any String, Integer, or Float pair where the first bound parses larger than the second, in DSL type expressions or Ruby PTimespanType.new(from, to).
Common situations: Bounds supplied as user variables in the wrong order (e.g. expiry window computed as [end, start]); DST or timezone shifts making a previously valid timestamp range inverted; literals written largest-first out of habit.
Related errors
- Unable to create a #{impl_class.name} from a #{arg.class.nam
- One or more file(s) specified did not exist: %{files}
- a data type must have an interface
- Resource instance does not match request key
- Instance name %{name} does not match requested key %{key}
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/4dc01e0ccfe0400c.
Report an issue: GitHub.