ankane/searchkick · error · Searchkick::Error
The `elasticsearch` gem must be 8+
Error message
The `elasticsearch` gem must be 8+
What it means
Searchkick's knn option builds a pure vector (kNN) query and only supports it on a match-all term ('*'). Combining a text term with knn in a single query would silently drop one of the two, so set_knn raises ArgumentError and requires hybrid text+vector search to be expressed as Searchkick.multi_search with a keyword query plus a separate vector query that you merge client-side.
Source
Thrown at lib/searchkick.rb:98
elsif defined?(OpenSearch::Client)
:opensearch
elsif defined?(Elasticsearch::Client)
:elasticsearch
else
raise Error, "No client found - install the `elasticsearch` or `opensearch-ruby` gem"
end
if client_type == :opensearch
OpenSearch::Client.new({
url: ENV["OPENSEARCH_URL"],
transport_options: {request: {timeout: timeout}},
retry_on_failure: 2
}.deep_merge(client_options)) do |f|
f.use Searchkick::Middleware
f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
end
else
raise Error, "The `elasticsearch` gem must be 8+" if Elasticsearch::VERSION.to_i < 8
Elasticsearch::Client.new({
url: ENV["ELASTICSEARCH_URL"],
transport_options: {request: {timeout: timeout}},
retry_on_failure: 2
}.deep_merge(client_options)) do |f|
f.use Searchkick::Middleware
f.request :aws_sigv4, signer_middleware_aws_params if aws_credentials
end
end
end
end
def self.env
@env ||= ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
end
def self.search_timeoutView on GitHub (pinned to 93e901a75b)
Solutions
- Use Searchkick.multi_search with two queries - one keyword, one vector - and interleave/merge the results yourself
- For pure vector search, pass term '*': Product.search('*', knn: {field: :embedding, vector: vec, distance: 'cosine'})
- To combine lexical filters with vectors, keep term '*' and use where: (it becomes the knn filter), not a text term
Example fix
# before
Product.search('apple', knn: {field: :embedding, vector: vec, distance: 'cosine'})
# => ArgumentError: Use Searchkick.multi_search for hybrid search
# after
queries = [
Product.search('apple'),
Product.search('*', knn: {field: :embedding, vector: vec, distance: 'cosine'})
]
keyword, vector = Searchkick.multi_search(queries)
# merge/rank keyword.results and vector.results yourself Defensive patterns
Strategy: validation
Validate before calling
def hybrid_search(term, vector)
if term.nil? || term == '*'
[Product.search('*', knn: {field: :embedding, vector: vector, distance: 'cosine'})]
else
[
Product.search(term),
Product.search('*', knn: {field: :embedding, vector: vector, distance: 'cosine'})
]
end
end
Searchkick.multi_search(hybrid_search(term, vec)) Try / catch
begin
Product.search(term, knn: knn_opts)
rescue ArgumentError
# fall back to multi_search hybrid path
Searchkick.multi_search([Product.search(term), Product.search('*', knn: knn_opts)])
end Prevention
- Keep vector search behind a helper that forces term '*' for the knn leg
- Centralize hybrid search in one service object so no call site mixes term with knn:
- Document the merge strategy (interleave, rrf) next to the helper
When it happens
Trigger: Product.search('apple', knn: {field: :embedding, vector: vec, distance: 'cosine'}) - any term other than '*' raises. The same call with '*' as the term works, because where-filters become the knn filter clause instead of a text query.
Common situations: Adding semantic/vector ranking to an existing keyword search and passing knn: alongside the user query; porting raw Elasticsearch DSL (bool + knn in one request) to Searchkick; assuming Searchkick performs native hybrid scoring in one request.
Related errors
- Use Searchkick.search to search multiple models
- Redis not configured
- Need primary key to load records
- #{class_name} is not a searchkick model
- Must specify a distance for OpenSearch
AI-assisted analysis of ankane/searchkick@93e901a75b (2026-08-21).
Data as JSON: /api/errors/68cf4bb2fdf4ec8d.
Report an issue: GitHub.