ankane/searchkick · error · Searchkick::Error
Cannot reindex object
Error message
Cannot reindex object
What it means
Inside where hashes, field: {op: value} supports a fixed operator set: :in, :exists, :not, and the range comparisons :gt, :gte, :lt, :lte. Any other key raises ArgumentError 'Unknown where operator: ...' with the offending operator inspected, so typos and SQL-style names fail fast at query-build time.
Source
Thrown at lib/searchkick/index.rb:225
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] || {})
end
if object.is_a?(Array)
# note: purposefully skip full
return reindex_records(object, method_name: method_name, ignore_missing: ignore_missing, **options)
end
if !object.respond_to?(:searchkick_klass)
raise Error, "Cannot reindex object"
end
scoped = Searchkick.relation?(object)
# call searchkick_klass for inheritance
relation = scoped ? object.all : Searchkick.scope(object.searchkick_klass).all
refresh = options.fetch(:refresh, !scoped)
options.delete(:refresh)
if method_name || (scoped && !full)
mode = options.delete(:mode) || :inline
scope = options.delete(:scope)
job_options = options.delete(:job_options)
raise ArgumentError, "unsupported keywords: #{options.keys.map(&:inspect).join(", ")}" if options.any?
# import only
import_scope(relation, method_name: method_name, mode: mode, scope: scope, ignore_missing: ignore_missing, job_options: job_options)
self.refresh if refreshView on GitHub (pinned to 93e901a75b)
Solutions
- Use the supported operators: {gt:}, {gte:}, {lt:}, {lte:}, {in: [...]}, {exists: true/false}, {not: ...}
- Negation is :not - where: {status: {not: 'sold'}} - not :ne/:not_eq
- Validate operator keys against a whitelist when filters are built from user input
Example fix
# before
Product.search('*', where: {price: {gteq: 10, ne: nil}})
# => ArgumentError: Unknown where operator: :gteq
# after
Product.search('*', where: {price: {gte: 10}, status: {not: 'sold'}}) Defensive patterns
Strategy: validation
Validate before calling
ALLOWED_OPS = %i[in exists not gt gte lt lte].freeze
def sanitize_where(field_ops)
field_ops.slice(*ALLOWED_OPS)
end
Product.search('*', where: {price: sanitize_where(params_ops)}) Try / catch
begin
Product.search('*', where: where_filters)
rescue ArgumentError => e
raise unless e.message.start_with?('Unknown where operator')
where_filters.transform_keys! { |k| ALIAS[k] || k } # e.g. eq -> gte-less designs; log unknown
retry
end Prevention
- Whitelist operator keys when where hashes are built from user or API input
- Keep a translation table from your API's filter vocabulary to Searchkick operators
- Add a unit test per operator you rely on
When it happens
Trigger: Product.search('*', where: {price: {eq: 5}}), where: {created_at: {after: date}} or where: {status: {ne: 'sold'}}. Valid names are :gt/:gte/:lt/:lte, :in, :exists, :not (plus :_or/:_and/:_not/:_script at the top level).
Common situations: Translating SQL or ActiveRecord conditions (eq/ne/like/after/between) to Searchkick; building operator hashes dynamically from user input; typos like :gteq or :greater_than.
Related errors
- Requires non-OSS version of Elasticsearch
- Multiple clients found - set Searchkick.client_type = :elast
- No client found - install the `elasticsearch` or `opensearch
- The `elasticsearch` gem must be 8+
- Use Searchkick.search to search multiple models
AI-assisted analysis of ankane/searchkick@93e901a75b (2026-08-21).
Data as JSON: /api/errors/249398c54b4e9452.
Report an issue: GitHub.