{"record":{"id":"7a9704302caad3a6","repo":"ankane/searchkick","slug":"multiple-clients-found-set-searchkick-client-typ","errorCode":null,"errorMessage":"Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch","messagePattern":"Multiple clients found - set Searchkick\\.client_type = :elasticsearch or :opensearch","errorType":"exception","errorClass":"Searchkick::Error","httpStatus":null,"severity":"error","filePath":"lib/searchkick.rb","lineNumber":79,"sourceCode":"    attr_accessor :search_method_name, :timeout, :models, :client_options, :redis, :index_prefix, :index_suffix, :queue_name, :model_options, :client_type, :parent_job\n    attr_writer :client, :env, :search_timeout\n    attr_reader :aws_credentials\n  end\n  self.search_method_name = :search\n  self.timeout = 10\n  self.models = []\n  self.client_options = {}\n  self.queue_name = :searchkick\n  self.model_options = {}\n  self.parent_job = \"ActiveJob::Base\"\n\n  def self.client\n    @client ||= begin\n      client_type =\n        if self.client_type\n          self.client_type\n        elsif defined?(OpenSearch::Client) && defined?(Elasticsearch::Client)\n          raise Error, \"Multiple clients found - set Searchkick.client_type = :elasticsearch or :opensearch\"\n        elsif defined?(OpenSearch::Client)\n          :opensearch\n        elsif defined?(Elasticsearch::Client)\n          :elasticsearch\n        else\n          raise Error, \"No client found - install the `elasticsearch` or `opensearch-ruby` gem\"\n        end\n\n      if client_type == :opensearch\n        OpenSearch::Client.new({\n          url: ENV[\"OPENSEARCH_URL\"],\n          transport_options: {request: {timeout: timeout}},\n          retry_on_failure: 2\n        }.deep_merge(client_options)) do |f|\n          f.use Searchkick::Middleware\n          f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials\n        end\n      else","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/ankane/searchkick/blob/93e901a75b11a25101668a616e006b158251b16e/lib/searchkick.rb#L61-L97","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before\nProduct.search('milk', boost_by_distance: {location: {scale: '5mi', factor: 2}})\n# => ArgumentError: boost_by_distance requires :origin\n\n# after\nProduct.search('milk', boost_by_distance: {\n  location: {origin: {lat: 37.8, lon: -122.4}, scale: '5mi', factor: 2}\n})","handlingStrategy":"validation","validationCode":"opts = {boost_by_distance: {location: {scale: '1mi'}}}\n(opts[:boost_by_distance] || {}).each do |field, attrs|\n  attrs = attrs.is_a?(Hash) ? attrs : {}\n  raise ArgumentError, \"boost_by_distance[#{field}] requires :origin\" if attrs[:origin].nil?\nend\nProduct.search('milk', **opts)","typeGuard":null,"tryCatchPattern":"begin\n  Product.search('milk', **user_opts)\nrescue ArgumentError => e\n  # config error in caller input - surface 400, do not retry\n  render_bad_request(e.message)\nend","preventionTips":["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"],"tags":["searchkick","boost-by-distance","geo-search","argumenterror","elasticsearch"],"backgroundTag":"missing-required-argument","analyzedSha":"93e901a75b11a25101668a616e006b158251b16e","analyzedAt":"2026-08-21T19:06:23.767Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}