{"record":{"id":"3ad76b79c1c18374","repo":"puppetlabs/puppet","slug":"type-specification-and-default-type-don-t-match","errorCode":null,"errorMessage":":type specification and default type don't match (default type is %{type_from_default})","messagePattern":":type specification and default type don't match \\(default type is %(.+?)\\)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/command_line/trollop.rb","lineNumber":215,"sourceCode":"          if opts[:default].empty?\n            raise ArgumentError, _(\"multiple argument type cannot be deduced from an empty array for '%{value0}'\") % { value0: opts[:default][0].class.name }\n          end\n\n          case opts[:default][0] # the first element determines the types\n          when Integer; :ints\n          when Numeric; :floats\n          when String; :strings\n          when IO; :ios\n          when Date; :dates\n          else\n            raise ArgumentError, _(\"unsupported multiple argument type '%{value0}'\") % { value0: opts[:default][0].class.name }\n          end\n        when nil; nil\n        else\n          raise ArgumentError, _(\"unsupported argument type '%{value0}'\") % { value0: opts[:default].class.name }\n        end\n\n      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\n\n      opts[:type] = opts[:type] || type_from_default || :flag\n\n      ## fill in :long\n      opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.tr(\"_\", \"-\")\n      opts[:long] =\n        case opts[:long]\n        when /^--([^-].*)$/\n          ::Regexp.last_match(1)\n        when /^[^-]/\n          opts[:long]\n        else\n          raise ArgumentError, _(\"invalid long option name %{name}\") % { name: opts[:long].inspect }\n        end\n      raise ArgumentError, _(\"long option name %{value0} is already taken; please specify a (different) :long\") % { value0: opts[:long].inspect } if @long[opts[:long]]\n\n      ## fill in :short\n      unless opts[:short] == :none","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/command_line/trollop.rb#L197-L233","documentation":"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.","triggerScenarios":"`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).","commonSituations":"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.","solutions":["Make the default's class agree with the declared :type (`default: 8080` for :int)","Convert at declaration time: `default: ENV['PORT'] && ENV['PORT'].to_i`","Omit :type and let Trollop infer it from the default","Omit :default and handle nil where the option is consumed"],"exampleFix":"# before\nopt :port, 'Server port', type: :int, default: ENV['PORT']  # ENV values are Strings -> mismatch\n\n# after\nopt :port, 'Server port', type: :int, default: ENV['PORT']&.to_i","handlingStrategy":"validation","validationCode":"# Check that :type agrees with the class of :default before calling opt\nEXPECTED_TYPE = { Integer => :int, Float => :float, String => :string,\n                  TrueClass => :flag, FalseClass => :flag, IO => :io, Date => :date }\n\ndef type_matches_default?(type, default)\n  base = type.to_s.sub('s', '').to_sym            # :ints -> :int, :floats -> :float\n  sample = default.is_a?(Array) ? default.first : default\n  EXPECTED_TYPE[sample.class] == base\nend\n\nabort \"type/default mismatch\" if opts[:type] && opts[:default] && !type_matches_default?(opts[:type], opts[:default])","typeGuard":"def inferred_type(default)\n  { Integer => :int, Float => :float, String => :string,\n    TrueClass => :flag, FalseClass => :flag, IO => :io, Date => :date }[default.class]\nend\n# Use inferred_type(default) as the :type you declare, or omit :type entirely","tryCatchPattern":"begin\n  parser.opt :port, 'Port', type: :int, default: raw\nrescue ArgumentError => e\n  # re-declare with the default converted to the declared type\n  parser.opt :port, 'Port', type: :int, default: Integer(raw)\nend","preventionTips":["Never mix a string default (ENV/YAML sourced) with a numeric :type - coerce first (to_i/to_f)","Prefer declaring only one of :type or :default and letting Trollop infer the other","Cover each option declaration with a parser-construction unit test"],"tags":["puppet","trollop","cli","option-parsing","type-mismatch"],"backgroundTag":"option-type-mismatch","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}