ankane/searchkick · error · ArgumentError
unknown keywords: #{unknown_keywords.join(", ")}
Error message
unknown keywords: #{unknown_keywords.join(", ")} What it means
Like the model macro, `Model.search(term, **options)` validates its keywords against a query-time allow-list (`:where, :order, :fields, :boost_by, :misspellings, :aggs, ...`). Unknown keys are collected and raised as one `ArgumentError` before any request is sent. Typical causes: model-level options passed at query time, misspellings, or options that only exist in other searchkick versions.
Source
Thrown at lib/searchkick/query.rb:33
:offset_value, :offset, :previous_page, :prev_page, :next_page, :first_page?, :last_page?,
:out_of_range?, :hits, :response, :to_a, :first, :scroll, :highlights, :with_highlights,
:with_score, :misspellings?, :scroll_id, :clear_scroll, :missing_records, :with_hit
def initialize(klass, term = "*", **options)
if options[:conversions]
Searchkick.warn("The `conversions` option is deprecated in favor of `conversions_v2`, which provides much better search performance. Upgrade to `conversions_v2` or rename `conversions` to `conversions_v1`")
end
if options.key?(:conversions_v1)
options[:conversions] = options.delete(:conversions_v1)
end
unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost,
:boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_v2, :conversions_term, :debug, :emoji, :exclude, :explain,
:fields, :highlight, :includes, :index_name, :indices_boost, :knn, :limit, :load,
:match, :misspellings, :models, :model_includes, :offset, :opaque_id, :operator, :order, :padding, :page, :per_page, :profile,
:request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where]
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
term = term.to_s
if options[:emoji]
term = EmojiParser.parse_unicode(term) { |e| " #{e.name.tr('_', ' ')} " }.strip
end
@klass = klass
@term = term
@options = options
@match_suffix = options[:match] || searchkick_options[:match] || "analyzed"
# prevent Ruby warnings
@type = nil
@routing = nil
@misspellings = false
@misspellings_below = nil
@highlighted_fields = nilView on GitHub (pinned to 93e901a75b)
Solutions
- Compare the listed unknown keys against the allow-list in lib/searchkick/query.rb:23-27 and fix names/locations.
- Move model-level options (`batch_size`, `searchable`, `filterable`, `language`, ...) into the `searchkick` macro; keep only query-shaping options in `.search`.
- Verify the option exists in your gem version (`Searchkick::VERSION` + the matching README tag on GitHub) and pin the Gemfile accordingly.
- Smoke-test one representative `.search` call in a staging console right after gem upgrades.
Example fix
# before
Product.search("milk", filter: {in_stock: true}, page: 1)
# => ArgumentError: unknown keywords: filter
# after
Product.search("milk", where: {in_stock: true}, page: 1) Defensive patterns
Strategy: validation
Validate before calling
QUERY_OPTIONS = %i[where order fields limit offset page per_page aggs boost_by misspellings operator highlight load includes exclude similar select body body_options index_name models knn].freeze
def product_search(term = "*", **opts)
unknown = opts.keys - QUERY_OPTIONS
raise ArgumentError, "unknown search options: #{unknown.join(', ')}" if unknown.any?
Product.search(term, **opts)
end Type guard
def valid_search_options?(opts) allowed = %i[aggs block body body_options boost boost_by boost_by_distance boost_by_recency boost_where conversions conversions_v2 conversions_term debug emoji exclude explain fields highlight includes index_name indices_boost knn limit load match misspellings models model_includes offset opaqueid operator order padding page per_page profile request_params routing scope_results scroll select similar smart_aggs suggest total_entries track type where] (opts.keys - allowed).empty? end
Prevention
- Route all searches through a thin wrapper that validates option keys, so typos fail loudly and centrally.
- After gem upgrades, grep the codebase for `.search(` usages with newly invalid options.
- Smoke-test one search per model in the staging console after every searchkick version bump.
When it happens
Trigger: `Product.search("milk", batch_size: 100)` (model option), `Product.search("milk", filter: {...})` (meant `where:`), `Product.search("milk", lang: "en")`, or any option added/removed across gem versions — raises at query construction, so the first search after deploy fails.
Common situations: Upgrading searchkick majors where options were renamed (e.g. old `misspellings: false` stays valid but others change); copying search calls from README HEAD on an older gem; confusing model-level options (`searchable`, `word_start`, `stemmer`) with query-level ones; passing a typo'd `where` as `whare`.
Related errors
- unknown keywords: #{unknown_keywords.join(", ")}
- 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/8ef7d0f06bf83f1d.
Report an issue: GitHub.