puppetlabs/puppet · error · ArgumentError

Could not parse '%{string}'

Error message

Could not parse '%{string}'

What it means

Each comma-separated entry inside a {...} file-options block must match /^\s*(\w+)\s*=\s*(\w+)\s*$/ — one word, an equals sign, one word. Anything else (empty value `mode =`, values containing dashes/slashes/dots/plus like group = puppet+shadow, trailing commas producing empty strings, stray text) raises ArgumentError 'Could not parse ...' with the whole original setting string for context.

Source

Thrown at lib/puppet/settings/config_file.rb:140

  def extract_fileinfo(string)
    result = {}
    value = string.sub(/\{\s*([^}]+)\s*\}/) do
      params = ::Regexp.last_match(1)
      params.split(/\s*,\s*/).each do |str|
        if str =~ /^\s*(\w+)\s*=\s*(\w+)\s*$/
          param = ::Regexp.last_match(1).intern
          value = ::Regexp.last_match(2)
          result[param] = value
          unless [:owner, :mode, :group].include?(param)
            raise ArgumentError, _("Invalid file option '%{parameter}'") % { parameter: param }
          end

          if param == :mode and value !~ /^\d+$/
            raise ArgumentError, _("File modes must be numbers")
          end
        else
          raise ArgumentError, _("Could not parse '%{string}'") % { string: string }
        end
      end
      ''
    end
    result[:value] = value.sub(/\s*$/, '')
    result
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. Keep every pair strictly as name = value with single-word values (letters, digits, underscore only).
  2. Remove trailing/leading commas and never leave an empty value; drop the pair entirely if unset.
  3. For values with punctuation (domain groups), precompute a plain name Puppet's regex accepts or manage ownership via a file resource instead.

Example fix

# before
vardir = /opt/puppet { owner = puppet, mode = 750, }

# after
vardir = /opt/puppet { owner = puppet, mode = 750 }
Defensive patterns

Strategy: validation

Validate before calling

block.split(/\s*,\s*/).each do |pair|
  raise ArgumentError, "unparseable file option pair: #{pair.inspect}" unless pair.match?(/^\s*(\w+)\s*=\s*(\w+)\s*$/)
end

Try / catch

begin
  Puppet::Settings::ConfigFile.parse_file(file, text, [])
rescue ArgumentError => e
  raise unless e.message.include?("Could not parse")
  Puppet.err("each {...} entry must be name = value with single-word values: #{e.message}")
  raise
end

Prevention

When it happens

Trigger: `logdir = /var/log/puppet { owner = puppet, }` (trailing comma -> empty pair); `{ mode = 750, group = domain\\users }` (backslashes not word chars); `{ owner = }` empty value; multi-word junk inside the braces.

Common situations: Hand-editing file-option blocks; templating that leaves blank values for optional pairs; AD/domain group names or paths pasted into group/owner options; comment text accidentally left inside braces.

Related errors


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