puppetlabs/puppet · error · ArgumentError

Unable to create a #{impl_class.name} from a #{arg.class.nam

Error message

Unable to create a #{impl_class.name} from a #{arg.class.name}

What it means

PAbstractTimeDataType#convert_arg accepts only: an instance of the impl class (Timespan/Timestamp), a Hash (via from_hash), nil or :default (unbounded), a String (parsed), and Integer/Float (seconds). Everything else - Array, Symbol, Object - reaches the else branch and raises ArgumentError naming the impl class and the argument's class. (The trailing unless in the source is dead code: nil and :default were already matched above.)

Source

Thrown at lib/puppet/pops/types/p_timespan_type.rb:74

      @from == -Float::INFINITY && @to == Float::INFINITY
    end

    def convert_arg(arg, min)
      case arg
      when impl_class
        arg
      when Hash
        impl_class.from_hash(arg)
      when nil, :default
        min ? -Float::INFINITY : Float::INFINITY
      when String
        impl_class.parse(arg)
      when Integer
        impl_class.new(arg * Time::NSECS_PER_SEC)
      when Float
        impl_class.new(arg * Time::NSECS_PER_SEC)
      else
        raise ArgumentError, "Unable to create a #{impl_class.name} from a #{arg.class.name}" unless arg.nil? || arg == :default

        nil
      end
    end

    # Concatenates this range with another range provided that the ranges intersect or
    # are adjacent. When that's not the case, this method will return `nil`
    #
    # @param o [PAbstractTimeDataType] the range to concatenate with this range
    # @return [PAbstractTimeDataType,nil] the concatenated range or `nil` when the ranges were apart
    # @api public
    def merge(o)
      if intersect?(o) || adjacent?(o)
        new_min = numeric_from <= o.numeric_from ? numeric_from : o.numeric_from
        new_max = numeric_to >= o.numeric_to ? numeric_to : o.numeric_to
        self.class.new(new_min, new_max)
      else
        nil

View on GitHub (pinned to e227c27540)

Solutions

  1. Use a supported bound form: Integer/Float seconds ('3600' as 3600), a parseable String like '1:30:00' / ISO-8601, an existing Timespan/Timestamp instance, or a Hash accepted by from_hash
  2. For field-wise construction prefer the DSL new dispatches (Timespan.new(days, hours, ...) or from_fields_hash) rather than the ranged type constructor
  3. Coerce unknown input to a canonical value (or nil for unbounded) before passing it as a bound

Example fix

# before
Puppet::Pops::Types::PTimespanType.new(nil, [:hours, 1])   # Array bound -> ArgumentError

# after
Puppet::Pops::Types::PTimespanType.new(nil, 3600)          # Float/Integer seconds
# or: PTimespanType.new(nil, '1:00:00')                    # parseable String
# or: Timespan.from_fields(false, 0, 1, 0, 0)              # field-wise construction
Defensive patterns

Strategy: type-guard

Validate before calling

# only pass bound forms convert_arg understands
allowed = [String, Integer, Float, Hash]
unless arg.nil? || arg == :default || allowed.any? { |c| arg.is_a?(c) } || arg.is_a?(Puppet::Pops::Time::Timespan)
  raise ArgumentError, "unsupported bound #{arg.class}; use String/Integer/Float seconds, Hash, or a Timespan"
end

Type guard

def time_bound_input?(arg)
  arg.nil? || arg == :default ||
    [String, Integer, Float, Hash].any? { |c| arg.is_a?(c) } ||
    arg.is_a?(Puppet::Pops::Time::Timespan) || arg.is_a?(Puppet::Pops::Time::Timestamp)
end

Try / catch

begin
  Puppet::Pops::Types::PTimespanType.new(from_arg, to_arg)
rescue ArgumentError => e
  raise unless e.message =~ /Unable to create a .* from a /
  raise "bad time bound #{to_arg.inspect}: pass seconds (Numeric), a parseable String, a Hash, or nil"
end

Prevention

When it happens

Trigger: Ruby-side construction of a ranged time type with an unsupported bound object: PTimespanType.new(nil, [:hours, 1]), PTimestampType.new(:now, nil). Also reachable from serialized/pcore data where a bound deserialized as an Array or Hash-with-wrong-shape is passed to convert_arg.

Common situations: Programmatically building Timespan/Timestamp bounds from structured data (arrays of parts) instead of the accepted forms; passing :now or custom sentinel symbols; Hash bounds whose keys do not match from_hash's expectations surfacing here only after other errors.

Related errors


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