activerecord-hackery/ransack · error · InvalidSearchError

Invalid search term #{key}

Error message

Invalid search term #{key}

What it means

Search#build iterates every key of the q params hash. A key must be 's'/'sorts', a whitelisted ransackable scope, or an attribute method recognized by the context (attribute + predicate over a searchable association). Otherwise, when unknown conditions are not ignored — Model.ransack! sets ignore_unknown_conditions: false per search, and the global config can too — Ransack raises InvalidSearchError with 'Invalid search term <key>'.

Source

Thrown at lib/ransack/search.rb:57

      @sorts ||= []
      @ignore_unknown_conditions = options[:ignore_unknown_conditions] == false ? false : true
      build(params.with_indifferent_access)
    end

    def result(opts = {})
      @context.evaluate(self, opts)
    end

    def build(params)
      collapse_multiparameter_attributes!(params).each do |key, value|
        if ['s'.freeze, 'sorts'.freeze].freeze.include?(key)
          send("#{key}=", value)
        elsif @context.ransackable_scope?(key, @context.object)
          add_scope(key, value)
        elsif base.attribute_method?(key)
          base.send("#{key}=", value)
        elsif !Ransack.options[:ignore_unknown_conditions] || !@ignore_unknown_conditions
          raise InvalidSearchError, "Invalid search term #{key}"
        end
      end
      self
    end

    def sorts=(args)
      case args
      when Array
        args.each do |sort|
          if sort.kind_of? Hash
            sort = Nodes::Sort.new(@context).build(sort)
          else
            sort = Nodes::Sort.extract(@context, sort)
          end
          self.sorts << sort if sort
        end
      when Hash
        args.each do |index, attrs|

View on GitHub (pinned to e82f6bab39)

Solutions

  1. Add the attribute to the model allowlist: def self.ransackable_attributes(auth_object = nil); %w[name ... full_name] ... end
  2. For scopes, add the scope name to ransackable_scopes whitelist (as symbols) and define the scope
  3. Use non-strict Model.ransack(params[:q]) instead of ransack! so unknown terms are silently dropped (default ignore_unknown_conditions: true)
  4. Rescue Ransack::InvalidSearchError in the controller, notify the user, and re-render with a clean search

Example fix

# before
# params: q => { "full_name_eq" => "Alice" }
Person.ransack!(params[:q])
# => Ransack::InvalidSearchError: Invalid search term full_name_eq

# after
class Person < ApplicationRecord
  def self.ransackable_attributes(auth_object = nil)
    %w[name full_name created_at]
  end
end
Person.ransack!(params[:q])
Defensive patterns

Strategy: try-catch

Validate before calling

# reject keys that are neither sorts, whitelisted scopes, nor searchable attributes
ALLOWED_SCOPES = %i[active].freeze

def strict_q_params(raw, klass)
  raw.select do |key, _|
    next true if %w[s sorts].include?(key)
    next true if klass.ransackable_scopes.map(&:to_s).include?(key)
    klass.ransackable_attributes.any? do |a|
      key.start_with?(a) && key != a # has a predicate suffix
    end
  end
end

@q = Person.ransack!(strict_q_params(params[:q], Person))

Type guard

def known_search_term?(key, klass)
  %w[s sorts].include?(key) ||
    klass.method_defined?(nil) ||
    klass.ransackable_attributes.any? { |a| key.to_s.start_with?(a + '_') } ||
    klass.ransackable_scopes.any? { |s| s.to_s == key }
end

Try / catch

begin
  @q = Person.ransack!(params[:q])
rescue Ransack::InvalidSearchError => e
  Rails.logger.warn("Rejected search term: #{e.message}")
  @q = Person.ransack  # empty search, user re-enters filters
end

Prevention

When it happens

Trigger: Person.ransack!(params[:q]) where params include q[full_name_eq]=x but full_name is not in Person.ransackable_attributes; q[enabled]=true where enabled is neither attribute-with-predicate nor whitelisted scope; stray keys like q[commit] or q[utf8] leaking in; an attribute on an association whose ransackable_associations whitelist excludes it.

Common situations: Adopting the strict ransack! convention after a security review; adding new searchable columns or ransackers but forgetting to update ransackable_attributes/ransackable_scopes allowlists; forms that submit extra fields (submit buttons, hidden CSRF-derived keys) inside the q namespace; users hand-editing URLs.

Related errors


AI-assisted analysis of activerecord-hackery/ransack@e82f6bab39 (2026-08-21). Data as JSON: /api/errors/933539b2d46cefb9. Report an issue: GitHub.