{"record":{"id":"f66a3a62128e2dd2","repo":"puppetlabs/puppet","slug":"my-caller-class-wrong-argument-type-obj-c","errorCode":null,"errorMessage":"#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable.","messagePattern":"#(.+?)\\(\\): wrong argument type \\(#(.+?); is not Iterable\\.","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/types/iterable.rb","lineNumber":36,"sourceCode":"    # `Integer`      - when positive, yields each value from zero to the given number\n    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.\n    # `PEnumtype`    - yields each possible value of the enum.\n    # `Range`        - yields an iterator for all elements in the range provided that the range start and end\n    #                  are both integers or both strings and start is less than end using natural ordering.\n    # `Dir`          - yields each name in the directory\n    #\n    # An `ArgumentError` is raised for all other objects.\n    #\n    # @param my_caller [Object] The calling object to reference in errors\n    # @param obj [Object] The object to produce an `Iterable` for\n    # @param infer_elements [Boolean] Whether or not to recursively infer all elements of obj. Optional\n    #\n    # @return [Iterable,nil] The produced `Iterable`\n    # @raise [ArgumentError] In case an `Iterable` cannot be produced\n    # @api public\n    def self.asserted_iterable(my_caller, obj, infer_elements = false)\n      iter = on(obj, nil, infer_elements)\n      raise ArgumentError, \"#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable.\" if iter.nil?\n\n      iter\n    end\n\n    # Produces an `Iterable` for one of the following types with the following characteristics:\n    #\n    # `String`       - yields each character in the string\n    # `Array`        - yields each element in the array\n    # `Hash`         - yields each key/value pair as a two element array\n    # `Integer`      - when positive, yields each value from zero to the given number\n    # `PIntegerType` - yields each element from min to max (inclusive) provided min < max and neither is unbounded.\n    # `PEnumtype`    - yields each possible value of the enum.\n    # `Range`        - yields an iterator for all elements in the range provided that the range start and end\n    #                  are both integers or both strings and start is less than end using natural ordering.\n    # `Dir`          - yields each name in the directory\n    #\n    # The value `nil` is returned for all other objects.\n    #","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/types/iterable.rb#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","Verify the producer of the value (hiera function, custom function) really returns the collection type you expect at every code path."],"exampleFix":"# before\neach($lookup('services', undef)) {|s| notify { $s } }\n\n# after\n$services = pick($lookup('services', undef), [])\neach($services) {|s| notify { $s } }","handlingStrategy":"type-guard","validationCode":"# Ruby — use the non-raising factory and check for nil\niter = Puppet::Pops::Types::Iterable.on(obj)\nraise ArgumentError, \"#{obj.inspect} is not iterable\" if iter.nil?","typeGuard":"# Puppet — restrict the parameter to iterable kinds\nfunction mymap(Variant[Collection, String, Integer[0, default]] $enum) { ... }\n\n# Ruby — cheap structural guard\ndef iterable?(o)\n  o.is_a?(String) || o.is_a?(Array) || o.is_a?(Hash) ||\n    (o.is_a?(Integer) && o >= 0) || o.is_a?(Range) ||\n    Puppet::Pops::Types::Iterable.on(o) != nil\nend","tryCatchPattern":"begin\n  Puppet::Pops::Types::Iterable.asserted_iterable(self, obj)\nrescue ArgumentError => e\n  fail(\"expected Array/Hash/String/Integer, got #{obj.class}: #{e.message}\")\nend","preventionTips":["Type iteration parameters as Collection/Array[...] in custom functions instead of Any.","Default lookups that feed iteration: pick($x, []) or a dedicated key with a guaranteed array."],"tags":["puppet","iteration","iterable","argument-validation","type-system"],"backgroundTag":"value-not-iterable","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}