puppetlabs/puppet · error · ArgumentError

unsupported argument type '%{value0}'

Error message

unsupported argument type '%{value0}'

What it means

Puppet vendors the Trollop option parser (lib/puppet/util/command_line/trollop.rb) behind PuppetOptionParser for the `puppet` executable and face options. When Parser#opt registers an option it infers the option type from the class of the :default value; the case at trollop.rb:188-213 only accepts nil, Integer, other Numeric, TrueClass/FalseClass, String, IO, and Date (Array defaults go through a separate branch). A default of any other class falls into the else branch at line 212 and raises ArgumentError. This is a declaration-time developer error raised before any command line is parsed.

Source

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

        when IO; :io
        when Date; :date
        when Array
          if opts[:default].empty?
            raise ArgumentError, _("multiple argument type cannot be deduced from an empty array for '%{value0}'") % { value0: opts[:default][0].class.name }
          end

          case opts[:default][0] # the first element determines the types
          when Integer; :ints
          when Numeric; :floats
          when String; :strings
          when IO; :ios
          when Date; :dates
          else
            raise ArgumentError, _("unsupported multiple argument type '%{value0}'") % { value0: opts[:default][0].class.name }
          end
        when nil; nil
        else
          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]]

View on GitHub (pinned to e227c27540)

Solutions

  1. Use a supported default class: String, Integer, Float, true/false, IO, Date, or an Array of those
  2. Drop :default and declare :type explicitly (e.g. `type: :string`), handling nil where the value is consumed
  3. For array defaults, make sure the first element is Integer/Numeric/String/IO/Date so the multi type can be deduced (otherwise the 'unsupported multiple argument type' variant at line 208 fires)
  4. Pass complex values as strings and decode them in a :callback block

Example fix

# before
opt :mode, 'Execution mode', default: :fast

# after
opt :mode, 'Execution mode', type: :string, default: 'fast'
Defensive patterns

Strategy: validation

Validate before calling

# Validate an option hash's :default before handing it to Parser#opt
SUPPORTED_DEFAULT_CLASSES = [Integer, Numeric, TrueClass, FalseClass, String, IO, Date]

def supported_default?(default)
  default.nil? ||
    SUPPORTED_DEFAULT_CLASSES.any? { |k| default.is_a?(k) } ||
    (default.is_a?(Array) && !default.empty? && supported_default?(default.first))
end

raise ArgumentError, "unsupported default #{opts[:default].class}" unless supported_default?(opts[:default])

Type guard

def trollop_default_type(default)
  case default
  when Integer then :int
  when Numeric then :float
  when true, false then :flag
  when String then :string
  when IO then :io
  when Date then :date
  when Array then %i[ints floats strings ios dates].find { |t| trollop_default_type(default.first).to_s.start_with?(t.to_s[0...-1]) || trollop_default_type(default.first) == t.to_s.singularize.to_sym }
  end
end
# returns nil for unsupported classes (e.g. Symbol, Hash) - treat nil as 'will raise'

Try / catch

# Declaration-time errors are ArgumentError; catch them while building the parser so one bad option does not kill the whole face load:
begin
  parser.opt :mode, 'Mode', default: raw_default
rescue ArgumentError => e
  raise "Invalid option declaration (:mode): #{e.message}"
end

Prevention

When it happens

Trigger: Calling `opt :mode, 'Mode', default: :fast` (Symbol), `default: {}` (Hash), `default: Time.now`, or `default: 1..5` (Range). Also a Hash default used together with :multi, since arrays are the only collection with type deduction.

Common situations: Writing a Puppet face or application and reusing option hashes written for Ruby's OptionParser, where arbitrary default objects were fine; defaults loaded from YAML that deserialize into unexpected classes; forgetting that only Integer/Float/boolean/String/IO/Date and arrays of those are supported.

Related errors


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