ankane/searchkick · error · Searchkick::Error

Unknown model for index: #{index}. Pass the `models` option

Error message

Unknown model for index: #{index}. Pass the `models` option to the search method.

What it means

When a search spans multiple indices, Searchkick must map each hit's `_index` back to ActiveRecord models to hydrate records. With no single `@klass`, it strips the timestamp suffix from the index name (results.rb:239) and looks the alias up in `options[:index_mapping]`, a mapping built only when a `models:` option was passed to the search (query.rb:70-76). An empty mapping or an alias missing from it raises `Unknown model for index: ...` before any records load.

Source

Thrown at lib/searchkick/results.rb:242

    def with_hit_and_missing_records
      @with_hit_and_missing_records ||= begin
        missing_records = []

        if options[:load]
          grouped_hits = hits.group_by { |hit, _| hit["_index"] }

          # determine models
          index_models = {}
          grouped_hits.each do |index, _|
            models =
              if @klass
                [@klass]
              else
                index_alias = index.split("_")[0..-2].join("_")
                Array((options[:index_mapping] || {})[index_alias])
              end
            raise Error, "Unknown model for index: #{index}. Pass the `models` option to the search method." unless models.any?
            index_models[index] = models
          end

          # fetch results
          results = {}
          grouped_hits.each do |index, index_hits|
            results[index] = {}
            index_models[index].each do |model|
              results[index].merge!(results_query(model, index_hits).to_a.index_by { |r| r.id.to_s })
            end
          end

          # sort
          results =
            hits.map do |hit|
              result = results[hit["_index"]][hit["_id"].to_s]
              if result && !(options[:load].is_a?(Hash) && options[:load][:dumpable])
                if (hit["highlight"] || options[:highlight]) && !result.respond_to?(:search_highlights)

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Pass the models explicitly: `Searchkick.search('milk', models: [Product, Store])` - this both targets the right indices and builds the alias-to-model mapping
  2. If you only need raw hit metadata (ids, scores, highlights), pass `load: false` so no model resolution happens
  3. If you set `index_name:` on the search call, replace it with `models:` or ensure every searched index belongs to a registered model
  4. Keep the `models:` list complete when using inheritance, since one index can map to multiple child models (query.rb:73-75)

Example fix

# before
Searchkick.search("milk") # searches "*,-.*"; hits cannot be mapped to models

# after
Searchkick.search("milk", models: [Product, Store])
# or, raw hits without DB hydration:
Searchkick.search("milk", models: [Product, Store], load: false)
Defensive patterns

Strategy: validation

Validate before calling

def global_search(term, models:)
  raise ArgumentError, "models: is required for multi-index search" if models.blank?
  Searchkick.search(term, models: models)
end

Try / catch

begin
  results = Searchkick.search(term)
rescue Searchkick::Error => e
  raise unless e.message.start_with?("Unknown model for index:")
  results = Searchkick.search(term, load: false) # raw hits, no DB hydration needed
end

Prevention

When it happens

Trigger: `Searchkick.search('milk')` with no `models:` - the query then targets the wildcard expression `"*,-.*"` (query.rb:87) so no mapping exists and any hit raises; passing a custom `index_name:` to the search whose indices are not registered models; a hit whose derived alias does not match any model in the `models:` list (e.g. unusual custom index names).

Common situations: Building site-wide search with Searchkick.search and forgetting the model list; searching alongside indices created outside Searchkick; changing index_prefix/suffix so derived aliases no longer match; upgrading to Searchkick 5+ where model-less searches hit the wildcard index expression.

Related errors


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