bblimke/webmock · error · TypeError

Can't convert #{new_query_values.class} into Hash.

Error message

Can't convert #{new_query_values.class} into Hash.

What it means

values_to_query serializes a Hash, an Array of [key, value] pairs, or anything responding to to_hash back into a query string; TypeError is raised for anything else (String, Integer, arbitrary object). nil is a documented short-circuit (returns nil), but an already-serialized query string is NOT accepted - this method is the inverse of query_to_values.

Source

Thrown at lib/webmock/util/query_mapper.rb:185

          else
            current_node[last_key] = value
          end
        end
      end

      ##
      # Sets the query component for this URI from a Hash object.
      # This method produces a query string using the :subscript notation.
      # An empty Hash will result in a nil query.
      #
      # @param [Hash, #to_hash, Array] new_query_values The new query values.
      def values_to_query(new_query_values, options = {})
        options[:notation] ||= :subscript
        return if new_query_values.nil?

        unless new_query_values.is_a?(Array)
          unless new_query_values.respond_to?(:to_hash)
            raise TypeError,
                  "Can't convert #{new_query_values.class} into Hash."
          end
          new_query_values = new_query_values.to_hash
          new_query_values = new_query_values.inject([]) do |object, (key, value)|
            key = key.to_s if key.is_a?(::Symbol) || key.nil?
            if value.is_a?(Array) && value.empty?
              object << [key.to_s + '[]']
            elsif value.is_a?(Array)
              value.each { |v| object << [key.to_s + '[]', v] }
            elsif value.is_a?(Hash)
              value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]}
            else
              object << [key.to_s, value]
            end
            object
          end
          # Useful default for OAuth and caching.
          # Only to be used for non-Array inputs. Arrays should preserve order.

View on GitHub (pinned to b187df8827)

Solutions

  1. Pass a Hash ({ 'a' => '1', 'b' => '2' }) or an Array of [key, value] pairs
  2. If you already have a query string, parse it first with query_to_values and pass the resulting Hash
  3. Call .to_h on duck-typed objects at the boundary before handing them over

Example fix

// before
query = WebMock::Util::QueryMapper.values_to_query('a=1&b=2')
# => TypeError: Can't convert String into Hash.

// after
query = WebMock::Util::QueryMapper.values_to_query({ 'a' => '1', 'b' => '2' })
# => a=1&b=2
Defensive patterns

Strategy: type-guard

Validate before calling

values = { 'a' => '1', 'b' => '2' } # a Hash, not the string 'a=1&b=2'
raise TypeError, 'values_to_query expects a Hash or Array of pairs' unless values.is_a?(Hash)
query = WebMock::Util::QueryMapper.values_to_query(values)

Type guard

def query_values_input?(value)
  value.is_a?(Hash) || value.is_a?(Array) || value.respond_to?(:to_hash)
end

Prevention

When it happens

Trigger: WebMock::Util::QueryMapper.values_to_query('a=1&b=2') (passing a query string where a Hash is required); passing an Integer, Symbol, or a Struct/Data object that lacks to_hash; adapters or helpers feeding serialized strings from config into the mapper during add_query_params or build_request_signature.

Common situations: Role confusion between query_to_values (parses strings) and values_to_query (serializes hashes); params round-tripped through JSON ending up as Strings; query strings received from one API handed straight to the mapper to re-serialize.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23). Data as JSON: /api/errors/b270593164a196ee. Report an issue: GitHub.