puppetlabs/puppet · error · ArgumentError

:type specification and default type don't match (default ty

Error message

:type specification and default type don't match (default type is %{type_from_default})

What it means

When Parser#opt receives both :type and :default, Trollop derives a second type symbol from the default's class (type_from_default) and compares it with the declared :type at trollop.rb:215. If they disagree, opt raises ArgumentError rather than silently coercing the default. Type aliases (:boolean/:bool, :integer, :integers, :double, :doubles) are normalized before the check, so only genuine class/type mismatches fire.

Source

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

          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]]

      ## fill in :short
      unless opts[:short] == :none

View on GitHub (pinned to e227c27540)

Solutions

  1. Make the default's class agree with the declared :type (`default: 8080` for :int)
  2. Convert at declaration time: `default: ENV['PORT'] && ENV['PORT'].to_i`
  3. Omit :type and let Trollop infer it from the default
  4. Omit :default and handle nil where the option is consumed

Example fix

# before
opt :port, 'Server port', type: :int, default: ENV['PORT']  # ENV values are Strings -> mismatch

# after
opt :port, 'Server port', type: :int, default: ENV['PORT']&.to_i
Defensive patterns

Strategy: validation

Validate before calling

# Check that :type agrees with the class of :default before calling opt
EXPECTED_TYPE = { Integer => :int, Float => :float, String => :string,
                  TrueClass => :flag, FalseClass => :flag, IO => :io, Date => :date }

def type_matches_default?(type, default)
  base = type.to_s.sub('s', '').to_sym            # :ints -> :int, :floats -> :float
  sample = default.is_a?(Array) ? default.first : default
  EXPECTED_TYPE[sample.class] == base
end

abort "type/default mismatch" if opts[:type] && opts[:default] && !type_matches_default?(opts[:type], opts[:default])

Type guard

def inferred_type(default)
  { Integer => :int, Float => :float, String => :string,
    TrueClass => :flag, FalseClass => :flag, IO => :io, Date => :date }[default.class]
end
# Use inferred_type(default) as the :type you declare, or omit :type entirely

Try / catch

begin
  parser.opt :port, 'Port', type: :int, default: raw
rescue ArgumentError => e
  # re-declare with the default converted to the declared type
  parser.opt :port, 'Port', type: :int, default: Integer(raw)
end

Prevention

When it happens

Trigger: `opt :port, 'Port', type: :int, default: '8080'` (String default vs :int); `type: :string, default: 5`; `type: :flag, default: 'yes'`; `type: :ints, default: [1.5]` (Float elements infer :floats).

Common situations: Defaults sourced from ENV or YAML so they arrive as strings (`default: ENV['PORT']`); copy-pasting a default from another option with a different type; refactoring an option's type without updating its default.

Related errors


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