ankane/searchkick · error · Searchkick::Error

#{class_name} is not a searchkick model

Error message

#{class_name} is not a searchkick model

What it means

For exact kNN on Elasticsearch, Searchkick maps the distance metric to a Painless script function (cosineSimilarity, l2norm, l1norm, dotProduct). Only 'cosine', 'euclidean', 'taxicab' and 'inner_product' are supported on this branch; any other value - including 'chebyshev', which only the OpenSearch branch supports - raises ArgumentError 'Unknown distance: ...'.

Source

Thrown at lib/searchkick.rb:307

        raise Error, "Need primary key to load records" if !primary_key

        relation.where(primary_key => ids)
      elsif relation.respond_to?(:queryable)
        relation.queryable.for_ids(ids)
      end

    raise Error, "Not sure how to load records" if !relation

    relation
  end

  # public (for reindexing conversions)
  def self.load_model(class_name, allow_child: false)
    model = class_name.safe_constantize
    raise Error, "Could not find class: #{class_name}" unless model
    if allow_child
      unless model.respond_to?(:searchkick_klass)
        raise Error, "#{class_name} is not a searchkick model"
      end
    else
      unless Searchkick.models.include?(model)
        raise Error, "#{class_name} is not a searchkick model"
      end
    end
    model
  end

  # private
  def self.indexer
    Thread.current[:searchkick_indexer] ||= Indexer.new
  end

  # private
  def self.callbacks_value
    Thread.current[:searchkick_callbacks_enabled]
  end

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Use 'cosine', 'euclidean', 'taxicab' or 'inner_product'
  2. For chebyshev on Elasticsearch, compute it via where: {_script: Searchkick::Script.new(...)} or switch the exact query to OpenSearch
  3. Verify exact lowercase spelling of the metric string

Example fix

# before
Product.search('*', knn: {field: :v, vector: vec, distance: 'chebyshev', exact: true})
# => ArgumentError: Unknown distance: chebyshev (Elasticsearch branch)

# after
Product.search('*', knn: {field: :v, vector: vec, distance: 'cosine', exact: true})
Defensive patterns

Strategy: type-guard

Type guard

ES_EXACT_DISTANCES = %w[cosine euclidean taxicab inner_product].freeze

def valid_es_exact_distance?(value)
  ES_EXACT_DISTANCES.include?(value)
end

raise ArgumentError, "bad distance: #{d}" unless valid_es_exact_distance?(d)
Product.search('*', knn: {field: :v, vector: vec, distance: d, exact: true})

Try / catch

begin
  Product.search('*', knn: opts)
rescue ArgumentError => e
  raise unless e.message.start_with?('Unknown distance')
  opts[:distance] = 'cosine'
  Product.search('*', knn: opts)
end

Prevention

When it happens

Trigger: Product.search('*', knn: {field: :embedding, vector: vec, distance: 'chebyshev', exact: true}) on Elasticsearch; values like 'dot_product', 'l2' or 'Cosine'.

Common situations: Sharing query code between OpenSearch and Elasticsearch deployments; porting metric names from Faiss or sentence-transformers examples; typos and casing mistakes.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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