ankane/searchkick · error · Searchkick::Error

search must be called on model, not relation

Error message

search must be called on model, not relation

What it means

`searchkick_search` is defined on the model's eigenclass and first checks `Searchkick.relation?(self)`, which is true when `klass.current_scope` is non-nil — i.e. when the method is invoked while an ActiveRecord scope is active (Relation#method_missing delegates `search` to the class inside `scoping { }`). Because searchkick deliberately ignores scopes when querying (it filters only via `where:`), calling search under an active scope would silently drop the scope's conditions, so it raises `Searchkick::Error` instead.

Source

Thrown at lib/searchkick/model.rb:71

          data
        end unless base.method_defined?(:search_data)

        def should_index?
          true
        end unless base.method_defined?(:should_index?)
      end

      class_eval do
        cattr_reader :searchkick_options, :searchkick_klass, instance_reader: false

        class_variable_set :@@searchkick_options, options.dup
        class_variable_set :@@searchkick_klass, self
        class_variable_set :@@searchkick_index_cache, Searchkick::IndexCache.new

        class << self
          def searchkick_search(term = "*", **options, &block)
            if Searchkick.relation?(self)
              raise Searchkick::Error, "search must be called on model, not relation"
            end

            Searchkick.search(term, model: self, **options, &block)
          end
          alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name

          def searchkick_index(name: nil)
            index_name = name || searchkick_klass.searchkick_index_name
            index_name = index_name.call if index_name.respond_to?(:call)
            index_cache = class_variable_get(:@@searchkick_index_cache)
            index_cache.fetch(index_name) { Searchkick::Index.new(index_name, searchkick_options) }
          end
          alias_method :search_index, :searchkick_index unless method_defined?(:search_index)

          def searchkick_reindex(method_name = nil, **options)
            searchkick_index.reindex(self, method_name: method_name, **options)
          end
          alias_method :reindex, :searchkick_reindex unless method_defined?(:reindex)

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Call search on the model class and move scope conditions into the query: `Product.search("milk", where: {active: true})`.
  2. For chained scopes, extract the scope's conditions or use `unscoped`: `Product.unscoped.search("milk")` (note: this drops the scope on purpose).
  3. If the model has a `default_scope` you never want applied to search, add `unscope: true` to `searchkick` options so searchkick unscopes internally.
  4. For Mongoid criteria, call `.search` on the document class, not a `Criteria` object.

Example fix

# before
Product.where(active: true).search("milk")
# => Searchkick::Error: search must be called on model, not relation

# after
Product.search("milk", where: {active: true})
Defensive patterns

Strategy: validation

Validate before calling

def safe_search(klass, term = "*", **options)
  raise Searchkick::Error, "call search on #{klass}, not a relation/scope" if Searchkick.relation?(klass)
  klass.search(term, **options)
end

# usage: safe_search(Product, "milk", where: {active: true})

Type guard

def searchable_receiver?(klass)
  !Searchkick.relation?(klass) # true when no scope is active on klass
end

Prevention

When it happens

Trigger: `Product.where(active: true).search("milk")`, `Product.published.search("milk")`, calling `search` inside `Model.scoping { }` / another scope's body, or on a model with a `default_scope` and no `unscope: true` option. Also Mongoid criteria scopes via the Mongoid branch of `relation?`.

Common situations: New teams chaining searchkick onto relation chains like any other scope (`Model.where(...).limit(5).search`); models with `default_scope` where every search raises; refactoring from `Model.search` to chained query builders; Mongoid apps using criteria.

Related errors


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