puppetlabs/puppet · error · TypeConversionError

odd number of arguments for Hash

Error message

odd number of arguments for Hash

What it means

The new() function on Puppet's Hash type can build a hash from a flat array of alternating keys and values (implemented with Hash[*from]). Ruby's Hash[] semantics require an even number of elements, so an odd-length array raises TypeConversionError with Ruby's classic 'odd number of arguments for Hash' wording. An empty array yields {} and a Hash passes through unchanged; only the flat array/iterable path with an odd count fails.

Source

Thrown at lib/puppet/pops/types/types.rb:2849

            else
              result.merge!(value)
            end
          else
            r = path[0..-2].reduce(result) { |memo, idx| (memo.is_a?(Array) || memo.has_key?(idx)) ? memo[idx] : memo[idx] = {} }
            r[path[-1]] = (all_hashes ? PHashType.array_as_hash(value) : value)
          end
        end
        result
      end

      def from_array(from)
        case from
        when Array
          if from.size == 0
            {}
          else
            unless from.size.even?
              raise TypeConversionError, _('odd number of arguments for Hash')
            end

            Hash[*from]
          end
        when Hash
          from
        else
          if PIterableType::DEFAULT.instance?(from)
            Hash[*Iterable.on(from).to_a]
          else
            t = TypeCalculator.singleton.infer(from).generalize
            raise TypeConversionError, _("Value of type %{type} cannot be converted to Hash") % { type: t }
          end
        end
      end
    end
  end

View on GitHub (pinned to e227c27540)

Solutions

  1. Fix the source data so every key has a value.
  2. Check the count before converting and fail with a message that shows the array: if size($flat) % 2 != 0 { fail(...) }.
  3. Truncate deliberately when a trailing element is known garbage: $flat = $flat[0, (size($flat) / 2) * 2].
  4. Build the hash from real 2-element pairs instead of a flat list, e.g. reduce over each_slice(2).

Example fix

# before (Puppet DSL)
$flat = split($content, ' ')   # may end up odd after bad input
$h = Hash.new($flat)           # TypeConversionError when odd

# after
if size($flat) % 2 != 0 {
  fail("expected key/value pairs, got odd array: ${flat}")
}
$h = Hash.new($flat)
Defensive patterns

Strategy: validation

Validate before calling

# Ruby
raise ArgumentError, 'odd pair array' unless flat.size.even?

# Puppet DSL
if size($flat) % 2 != 0 { fail('odd number of elements; expected key/value pairs') }

Type guard

def even_pair_array?(a)
  a.is_a?(Array) && a.size.even?
end

Try / catch

begin
  h = Hash.new($flat)
rescue Puppet::Error
  # repair by dropping the dangling element, then retry
  h = Hash.new($flat[0, (size($flat) / 2) * 2])
end

Prevention

When it happens

Trigger: Hash.new(['a', 1, 'b']) in a manifest; Hash.new($flat) where $flat came from split() and has an odd element count; converting any iterable whose element count is odd via Hash.new.

Common situations: Parsing key/value tokens out of a config line where a trailing element or malformed input makes the count odd; module parameters accepting flat pair lists from Hiera; glue code that flattens pairs from another data source and loses one element.

Related errors


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