ankane/searchkick · error · Searchkick::Error

Multiple clients found - set Searchkick.client_type = :elast

Error message

Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch

What it means

Searchkick's boost_by_distance option builds an Elasticsearch/OpenSearch distance-decay scoring function (gauss by default) centered on an :origin point. Every field entry in the boost_by_distance hash must include :origin, otherwise the decay function has no reference location and Searchkick raises ArgumentError in set_boost_by_distance while building the query, before any request is sent.

Source

Thrown at lib/searchkick.rb:79

    attr_accessor :search_method_name, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options, :client_type, :parent_job
    attr_writer :client, :env, :search_timeout
    attr_reader :aws_credentials
  end
  self.search_method_name = :search
  self.timeout = 10
  self.models = []
  self.client_options = {}
  self.queue_name = :searchkick
  self.model_options = {}
  self.parent_job = "ActiveJob::Base"

  def self.client
    @client ||= begin
      client_type =
        if self.client_type
          self.client_type
        elsif defined?(OpenSearch::Client) && defined?(Elasticsearch::Client)
          raise Error, "Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch"
        elsif defined?(OpenSearch::Client)
          :opensearch
        elsif defined?(Elasticsearch::Client)
          :elasticsearch
        else
          raise Error, "No client found - install the `elasticsearch` or `opensearch-ruby` gem"
        end

      if client_type == :opensearch
        OpenSearch::Client.new({
          url: ENV["OPENSEARCH_URL"],
          transport_options: {request: {timeout: timeout}},
          retry_on_failure: 2
        }.deep_merge(client_options)) do |f|
          f.use Searchkick::Middleware
          f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
        end
      else

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Add :origin to each field's attributes: boost_by_distance: {location: {origin: {lat: 37.8, lon: -122.4}, scale: '1mi'}}
  2. If origin comes from request params, validate presence (params.require(:ll)) or default it to a fixed location before calling search
  3. Check key spelling - only :origin, :scale, :factor, :function are read from the attributes hash

Example fix

# before
Product.search('milk', boost_by_distance: {location: {scale: '5mi', factor: 2}})
# => ArgumentError: boost_by_distance requires :origin

# after
Product.search('milk', boost_by_distance: {
  location: {origin: {lat: 37.8, lon: -122.4}, scale: '5mi', factor: 2}
})
Defensive patterns

Strategy: validation

Validate before calling

opts = {boost_by_distance: {location: {scale: '1mi'}}}
(opts[:boost_by_distance] || {}).each do |field, attrs|
  attrs = attrs.is_a?(Hash) ? attrs : {}
  raise ArgumentError, "boost_by_distance[#{field}] requires :origin" if attrs[:origin].nil?
end
Product.search('milk', **opts)

Try / catch

begin
  Product.search('milk', **user_opts)
rescue ArgumentError => e
  # config error in caller input - surface 400, do not retry
  render_bad_request(e.message)
end

Prevention

When it happens

Trigger: Calling search with a boost_by_distance field hash that lacks :origin, e.g. Product.search('milk', boost_by_distance: {location: {scale: '5mi', factor: 2}}). Also the legacy format boost_by_distance: {field: :location, scale: '10mi'} without origin, and a nil origin (origin: params[:ll] when the param is missing).

Common situations: Copying a geo-boost example and dropping the origin line; sourcing origin from user coordinates that can be nil; typos like orgin:/center:/near:; switching from a geo where-filter (which needs no origin) to boosting and forgetting origin is required.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of ankane/searchkick@93e901a75b (2026-08-21). Data as JSON: /api/errors/7a9704302caad3a6. Report an issue: GitHub.