ankane/searchkick · error · Searchkick::Error

Requires non-OSS version of Elasticsearch

Error message

Requires non-OSS version of Elasticsearch

What it means

The exists where-operator accepts only true or false, mapping to an exists filter or a must_not-exists filter respectively. Any other value raises ArgumentError because Elasticsearch has no meaningful query for exists: nil, exists: 'true' or exists: 1.

Source

Thrown at lib/searchkick/index.rb:198

      RecordData.new(self, record).document_type
    end

    def similar_record(record, **options)
      options[:per_page] ||= 10
      options[:similar] = [RecordData.new(self, record).record_data]
      options[:models] ||= [record.class] unless options.key?(:model)

      Searchkick.search("*", **options)
    end

    def reload_synonyms
      if Searchkick.opensearch?
        client.transport.perform_request "POST", "_plugins/_refresh_search_analyzers/#{CGI.escape(name)}"
      else
        begin
          client.transport.perform_request("GET", "#{CGI.escape(name)}/_reload_search_analyzers")
        rescue => e
          raise Error, "Requires non-OSS version of Elasticsearch" if Searchkick.not_allowed_error?(e)
          raise e
        end
      end
    end

    # queue

    def reindex_queue
      ReindexQueue.new(name)
    end

    # reindex

    # note: this is designed to be used internally
    # so it does not check object matches index class
    def reindex(object, method_name: nil, ignore_missing: nil, full: false, **options)
      if @options[:job_options]
        options[:job_options] = (@options[:job_options] || {}).merge(options[:job_options] || {})

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Pass real booleans: where: {description: {exists: true}}
  2. Coerce params first: ActiveModel::Type::Boolean.new.cast(params[:has_desc]) or params == 'true'
  3. To match missing values use exists: false rather than exists: nil

Example fix

# before
Product.search('*', where: {description: {exists: params[:has_desc]}}) # params value can be nil/'true'
# => ArgumentError: Passing a value other than true or false to exists is not supported

# after
Product.search('*', where: {description: {exists: ActiveModel::Type::Boolean.new.cast(params[:has_desc])}})
Defensive patterns

Strategy: type-guard

Type guard

BOOL = ->(v) { v.nil? ? nil : ActiveModel::Type::Boolean.new.cast(v) }

def exists_filter(v)
  bool = BOOL.call(v)
  raise ArgumentError, 'exists must be true/false' unless bool == true || bool == false
  {exists: bool}
end

Product.search('*', where: {description: exists_filter(params[:has_desc])})

Prevention

When it happens

Trigger: Product.search('*', where: {description: {exists: nil}}); where: {email: {exists: 'true'}} or {exists: 1} when filters are built from unconverted request params.

Common situations: Building filters from query strings where booleans arrive as 'true'/'false' strings or nil; JSON payloads using 0/1 flags; assuming nil works like false (use exists: false for missing fields).

Related errors


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