puppetlabs/puppet · error · ArgumentError

a short option name can't be a number or a dash

Error message

a short option name can't be a number or a dash

What it means

INVALID_SHORT_ARG_REGEX is /[\d-]/ (trollop.rb:70). After the duplicate-character check, opt raises ArgumentError at trollop.rb:244 when the short option character is a digit or a dash. Digits are forbidden because a token like '-5' must stay consumable as a parameter (for example a negative number value for another option), and '-' is ambiguous with stdin-style conventions.

Source

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

          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?

      ## autobox :default for :multi (multi-occurrence) arguments
      opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)

      ## fill in :multi
      opts[:multi] ||= false

      opts[:desc] ||= desc
      @long[opts[:long]] = name
      @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
      @specs[name] = opts
      @order << [:opt, name]
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Use a letter for the short form (`short: 'n'`)
  2. Drop :short and rely on the long form only
  3. Pass `short: :none` to skip the short form entirely

Example fix

# before
opt :level, 'Level', short: '3'

# after
opt :level, 'Level', short: 'l'
Defensive patterns

Strategy: validation

Validate before calling

# Reject digit/dash short names before opt (mirrors INVALID_SHORT_ARG_REGEX /[\d-]/)
raise ArgumentError, 'short option cannot be a digit or dash' if opts[:short].to_s =~ /[\d-]/ && opts[:short].to_s.length == 1

Try / catch

begin
  parser.opt name, desc, short: candidate
rescue ArgumentError => e
  raise unless e.message.include?("can't be a number or a dash")
  parser.opt name, desc, short: candidate.to_s.tr('0-9-', 'abc').chars.first  # or just drop it
end

Prevention

When it happens

Trigger: `short: '3'`, `short: '0'`, or `short: '-'`.

Common situations: Numeric options where a digit feels mnemonic ('2' for two-way sync); porting legacy CLIs in the tar/od style whose classic flags were digits.

Related errors


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