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
elseView on GitHub (pinned to 93e901a75b)
Solutions
- Add :origin to each field's attributes: boost_by_distance: {location: {origin: {lat: 37.8, lon: -122.4}, scale: '1mi'}}
- If origin comes from request params, validate presence (params.require(:ll)) or default it to a fixed location before calling search
- 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
- Treat origin like a required request parameter when geo-boosting: validate presence and numeric lat/lon before search
- Wrap dynamic option hashes from params in a small sanitizer that whitelists keys (origin, scale, factor, function)
- Add a request spec covering the geo-boost search with and without coordinates
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
- No client found - install the `elasticsearch` or `opensearch
- #{class_name} is not a searchkick model
- The `elasticsearch` gem must be 8+
- Use Searchkick.search to search multiple models
- Need primary key to load records
AI-assisted analysis of ankane/searchkick@93e901a75b (2026-08-21).
Data as JSON: /api/errors/7a9704302caad3a6.
Report an issue: GitHub.