puppetlabs/puppet · error · ArgumentError

long option name is already taken; please specify a…

Error message

long option name %{value0} is already taken; please specify a (different) :long

What it means

The parser keeps an @long registry mapping long names to option symbols. After validating the long name's shape, opt raises ArgumentError at trollop.rb:230 if that name was already registered by a previous opt call. Collisions occur with an explicitly duplicated :long and, less obviously, when two different symbols normalize to the same long name because underscores are converted to dashes.

Solutions

  1. Rename one of the colliding options (its symbol or :long) so every long name is unique
  2. Delete the duplicate declaration and keep a single canonical option
  3. When overriding the behavior of an existing option, reuse the same symbol instead of registering a second one

Example fix

# before
opt :dry_run, 'Do a dry run'
opt :'dry-run', 'Alias for dry run'   # both map to '--dry-run' -> raise

# after
opt :dry_run, 'Do a dry run'          # only '--dry-run'
Defensive patterns

Strategy: validation

Validate before calling

# Before declaring, compute the long name the way Trollop does and check uniqueness
normalized = ->(name, long = nil) { (long || name.to_s).to_s.sub(/^--/, '').tr('_', '-') }

taken = Set.new
long = normalized.call(name, opts[:long])
if taken.include?(long)
  raise ArgumentError, "long option '--#{long}' already declared"
end
taken << long

Try / catch

begin
  parser.opt name, desc, opts
rescue ArgumentError => e
  raise unless e.message.include?('already taken')
  opts = opts.merge(long: "#{opts[:long] || name}-2")  # disambiguate deliberately
  retry
end

Prevention

When it happens

Trigger: `opt :dry_run` followed by `opt :'dry-run'` — both normalize to 'dry-run'; two options both declaring `long: 'verbose'`; re-declaring an option after reopening a face/application on a Parser instance that already has it.

Common situations: Monkey-patching or reopening a Puppet face that already declared the option; mixing :some_option and :'some-option' spellings across files; copy-pasted option blocks between faces.

Related errors


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

Appendix: source

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

          raise ArgumentError, _("unsupported argument type '%{value0}'") % { value0: opts[:default].class.name }
        end

      raise ArgumentError, _(":type specification and default type don't match (default type is %{type_from_default})") % { type_from_default: type_from_default } if opts[:type] && type_from_default && opts[:type] != type_from_default

      opts[:type] = opts[:type] || type_from_default || :flag

      ## fill in :long
      opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.tr("_", "-")
      opts[:long] =
        case opts[:long]
        when /^--([^-].*)$/
          ::Regexp.last_match(1)
        when /^[^-]/
          opts[:long]
        else
          raise ArgumentError, _("invalid long option name %{name}") % { name: opts[:long].inspect }
        end
      raise ArgumentError, _("long option name %{value0} is already taken; please specify a (different) :long") % { value0: opts[:long].inspect } if @long[opts[:long]]

      ## fill in :short
      unless opts[:short] == :none
        opts[:short] = opts[:short].to_s if opts[:short]
      end
      opts[:short] = case opts[:short]
                     when /^-(.)$/; ::Regexp.last_match(1)
                     when nil, :none, /^.$/; opts[:short]
                     else raise ArgumentError, _("invalid short option name '%{name}'") % { name: opts[:short].inspect }
                     end

      if opts[:short]
        raise ArgumentError, _("short option name %{value0} is already taken; please specify a (different) :short") % { value0: opts[:short].inspect } if @short[opts[:short]]
        raise ArgumentError, _("a short option name can't be a number or a dash") if opts[:short] =~ INVALID_SHORT_ARG_REGEX
      end

      ## fill in :default for flags
      opts[:default] = false if opts[:type] == :flag && opts[:default].nil?

View on GitHub (pinned to e227c27540)