puppetlabs/puppet · error · ArgumentError

#{my_caller.class}(): wrong argument type (#{obj.class}; is

Error message

#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable.

What it means

All of Puppet's iteration functions (each, map, filter, reduce, all, any, slice, step, reverse_each, index) obtain their iterator through Iterable.asserted_iterable. Iterable.on returns nil for anything that is not a String, Array, Hash, non-negative Integer, integer/string Range, Dir, Iterable/IteratorProducer, or the corresponding Puppet types (PIntegerType, PEnumType); asserted_iterable turns that nil into an ArgumentError naming the calling function.

Source

Thrown at lib/puppet/pops/types/iterable.rb:36

    # `Integer`      - when positive, yields each value from zero to the given number
    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.
    # `PEnumtype`    - yields each possible value of the enum.
    # `Range`        - yields an iterator for all elements in the range provided that the range start and end
    #                  are both integers or both strings and start is less than end using natural ordering.
    # `Dir`          - yields each name in the directory
    #
    # An `ArgumentError` is raised for all other objects.
    #
    # @param my_caller [Object] The calling object to reference in errors
    # @param obj [Object] The object to produce an `Iterable` for
    # @param infer_elements [Boolean] Whether or not to recursively infer all elements of obj. Optional
    #
    # @return [Iterable,nil] The produced `Iterable`
    # @raise [ArgumentError] In case an `Iterable` cannot be produced
    # @api public
    def self.asserted_iterable(my_caller, obj, infer_elements = false)
      iter = on(obj, nil, infer_elements)
      raise ArgumentError, "#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable." if iter.nil?

      iter
    end

    # Produces an `Iterable` for one of the following types with the following characteristics:
    #
    # `String`       - yields each character in the string
    # `Array`        - yields each element in the array
    # `Hash`         - yields each key/value pair as a two element array
    # `Integer`      - when positive, yields each value from zero to the given number
    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.
    # `PEnumtype`    - yields each possible value of the enum.
    # `Range`        - yields an iterator for all elements in the range provided that the range start and end
    #                  are both integers or both strings and start is less than end using natural ordering.
    # `Dir`          - yields each name in the directory
    #
    # The value `nil` is returned for all other objects.
    #

View on GitHub (pinned to e227c27540)

Solutions

  1. Give the value a safe default before iterating: $items = pick_default($lookup, []) (or `unless $x == undef` guards) so the argument is always a real Array/Hash.
  2. Convert the value to an iterable kind first: Integer($n) for a numeric loop, .to_a for a range-like value, Sensitive.unwrap for sensitive data.
  3. Verify the producer of the value (hiera function, custom function) really returns the collection type you expect at every code path.

Example fix

# before
each($lookup('services', undef)) {|s| notify { $s } }

# after
$services = pick($lookup('services', undef), [])
each($services) {|s| notify { $s } }
Defensive patterns

Strategy: type-guard

Validate before calling

# Ruby — use the non-raising factory and check for nil
iter = Puppet::Pops::Types::Iterable.on(obj)
raise ArgumentError, "#{obj.inspect} is not iterable" if iter.nil?

Type guard

# Puppet — restrict the parameter to iterable kinds
function mymap(Variant[Collection, String, Integer[0, default]] $enum) { ... }

# Ruby — cheap structural guard
def iterable?(o)
  o.is_a?(String) || o.is_a?(Array) || o.is_a?(Hash) ||
    (o.is_a?(Integer) && o >= 0) || o.is_a?(Range) ||
    Puppet::Pops::Types::Iterable.on(o) != nil
end

Try / catch

begin
  Puppet::Pops::Types::Iterable.asserted_iterable(self, obj)
rescue ArgumentError => e
  fail("expected Array/Hash/String/Integer, got #{obj.class}: #{e.message}")
end

Prevention

When it happens

Trigger: Passing a non-iterable first argument to an iteration function: each(1.5) {|x| ... }, map(true) {|x| ... }, each(undef) {...}, or iterating a value whose lookup/function call returned nil, a Float, a Boolean, or an arbitrary object. Also direct Ruby calls to Iterable.asserted_iterable(caller, obj) with such values.

Common situations: A hiera lookup or resource attribute expected to be an array comes back undef; a function returns Float or Boolean instead of Integer; iterating a Sensitive value without unwrapping; treating a single scalar like an array after a bad conditional.

Related errors


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