puppetlabs/puppet · error · CommandlineError

option '%{arg}' specified multiple times

Error message

option '%{arg}' specified multiple times

What it means

While resolving tokens, parse records each option symbol in given_args; if the same symbol appears again and the option was not declared with :multi => true, parse raises CommandlineError at trollop.rb:369 instead of applying last-wins semantics. Multi options accumulate values; single options treat a repeat as a user error. PuppetOptionParser registers puppet's own options with :multi => true (puppet_option_parser.rb:57), so repeats of core puppet flags are allowed and the last value wins — this error mainly affects tools and faces that call the vendored Trollop directly.

Source

Thrown at lib/puppet/util/command_line/trollop.rb:369

                  partial_match = @long[::Regexp.last_match(1).tr('-', '_')] || @long[::Regexp.last_match(1).tr('_', '-')] || @long["[no-]#{::Regexp.last_match(1).tr('-', '_')}"] || @long["[no-]#{::Regexp.last_match(1).tr('_', '-')}"]
                  if partial_match
                    Puppet.deprecation_warning _("Partial argument match detected: correct argument is %{partial_match}, got %{arg}. Partial argument matching is deprecated and will be removed in a future release.") % { arg: arg, partial_match: partial_match }
                  end
                  partial_match
                else
                  possible_match
                end
              else
                raise CommandlineError, _("invalid argument syntax: '%{arg}'") % { arg: arg }
              end

        unless sym
          next 0 if ignore_invalid_options
          raise CommandlineError, _("unknown argument '%{arg}'") % { arg: arg } unless sym
        end

        if given_args.include?(sym) && !@specs[sym][:multi]
          raise CommandlineError, _("option '%{arg}' specified multiple times") % { arg: arg }
        end

        given_args[sym] ||= {}

        given_args[sym][:arg] = arg
        given_args[sym][:params] ||= []

        # The block returns the number of parameters taken.
        num_params_taken = 0

        unless params.nil?
          if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
            given_args[sym][:params] << params[0, 1]  # take the first parameter
            num_params_taken = 1
          elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
            given_args[sym][:params] << params        # take all the parameters
            num_params_taken = params.size
          end

View on GitHub (pinned to e227c27540)

Solutions

  1. Pass the option once with the desired value
  2. Declare the option with `:multi => true` if repeats should be accepted (values accumulate, or last wins for single-arg types)
  3. De-duplicate the option in your wrapper or ARGV pre-processing before invoking the command

Example fix

# before
opt :server, 'Server host', type: :string   # no :multi
$ mytool --server a --server b -> raise

# after
opt :server, 'Server host', type: :string, multi: true
Defensive patterns

Strategy: validation

Validate before calling

# De-duplicate single-shot options in argv before parsing
seen = {}
cleaned = []
value_taking = ->(t) { t =~ /^--\S+=$/ || t =~ /^--no-/ || t == '--' }   # adjust to your option set
tokens = argv.dup
until tokens.empty?
  t = tokens.shift
  if (m = t.match(/^--([^=]+)(?:=(.*))?$/)) && !multi_options.include?(m[1].tr('-', '_').to_sym)
    cleaned.reject! { |c| c =~ /^--#{m[1]}=/ }   # last wins, like explicit :multi
    cleaned << t
    cleaned << tokens.shift if m[2].nil? && next_is_value(tokens.first)   # keep its value
  else
    cleaned << t
  end
end

Try / catch

begin
  opts = parser.parse(argv)
rescue Puppet::Util::CommandLine::Trollop::CommandlineError => e
  raise unless e.message.include?('specified multiple times')
  opts = parser.parse(argv.each_with_object({}) { |t, h| (h[t[/^--[^=]+/] || t] ||= []) << t }.values.map(&:last))
end

Prevention

When it happens

Trigger: `mytool --server a --server b` where :server was declared without :multi; a wrapper script appending a default flag onto a user-supplied ARGV that already contains it; an alias or config layer re-invoking the same option.

Common situations: Wrapper scripts that blindly append default flags; users overriding a value already injected by a profile or environment; combining config-file and CLI sources that map to the same option.

Related errors


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