puppetlabs/puppet · error · ArgumentError

Key/value pairs must be separated by '%{separator}'

Error message

Key/value pairs must be separated by '%{separator}'

What it means

For KeyValue properties, a String should-value must contain the property's separator ('=' by default; the class-level separator method can be overridden) so it can later be split into key and value (hashify_should calls split(separator)). A string without the separator raises ArgumentError naming the expected one. The check uses include?, so containing the character anywhere passes validation - but parsing still misbehaves if it is not between key and value.

Source

Thrown at lib/puppet/property/keyvalue.rb:139

      def insync?(is)
        return true unless is

        (is == should)
      end

      # We only accept an array of key/value pairs (strings), a single
      # key/value pair (string) or a Hash as valid values for our property.
      # Note that for an array property value, the 'value' passed into the
      # block corresponds to the array element.
      validate do |value|
        unless value.is_a?(String) || value.is_a?(Hash)
          raise ArgumentError, _("The %{name} property must be specified as a hash or an array of key/value pairs (strings)!") % { name: name }
        end

        next if value.is_a?(Hash)

        unless value.include?(separator.to_s)
          raise ArgumentError, _("Key/value pairs must be separated by '%{separator}'") % { separator: separator }
        end
      end

      # The validate step ensures that our passed-in value is
      # either a String or a Hash. If our value's a string,
      # then nothing else needs to be done. Otherwise, we need
      # to stringify the hash's keys and values to match our
      # internal representation of the property's value.
      munge do |value|
        next value if value.is_a?(String)

        munged_value = value.to_a.map! do |hash_key, hash_value|
          [hash_key.to_s.strip.to_sym, hash_value.to_s]
        end

        munged_value.to_h
      end
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Format values as key<separator>value matching the property's separator (default '=').
  2. If the separator is customizable, align the property class and the data.
  3. Pass a Hash instead of strings to sidestep separator handling entirely.

Example fix

# before (Puppet DSL)
mytype { 'x': attrs => 'justakey' }    # ArgumentError: must be separated by '='

# after
mytype { 'x': attrs => 'justakey=1' }  # or attrs => { 'justakey' => '1' }
Defensive patterns

Strategy: validation

Validate before calling

sep = '='   # use the property's separator method value
fail "missing separator '#{sep}' in #{s}" unless s.include?(sep)

# Puppet DSL
unless $s =~ /=/ { fail("value '${s}' must be key=value") }

Type guard

def keyvalue_pair?(s, sep = '=')
  s.is_a?(String) && s.include?(sep)
end

Prevention

When it happens

Trigger: attrs => 'justakey' (no '=' anywhere); the property class overrides separator to ':' but the data still uses '='; values produced by join/concat without inserting the separator.

Common situations: Separator mismatches between the property class and the data; hand-written Hiera values; plain identifiers supplied where a pair was expected.

Related errors


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