activerecord-hackery/ransack · error · InvalidSearchError

No valid predicate for #{key}

Error message

No valid predicate for #{key}

What it means

While parsing each q[...] condition key, Ransack strips a trailing predicate suffix (like _cont or _gteq) and looks it up with Predicate.named. If no known predicate remains — because the key has no suffix or an unrecognized one — Ransack raises InvalidSearchError, but only when unknown conditions are not being ignored (ransack! or ignore_unknown_conditions = false; plain Model.ransack ignores them by default).

Source

Thrown at lib/ransack/nodes/condition.rb:43

            # TODO: Figure out what to do with multiple types of attributes,
            # if anything. Tempted to go with "garbage in, garbage out" here.
            if predicate.validate(condition.values, condition.default_type)
              condition
            else
              nil
            end
          end
        end

        private

          def extract_values_for_condition(key, context = nil)
            str = key.dup
            name = Predicate.detect_and_strip_from_string!(str)
            predicate = Predicate.named(name)

            unless predicate || Ransack.options[:ignore_unknown_conditions]
              raise InvalidSearchError, "No valid predicate for #{key}"
            end

            if context.present?
              str = context.ransackable_alias(str)
            end

            combinator =
            if str.match(/_(or|and)_/)
              $1
            else
              nil
            end

            if context.present? && context.attribute_method?(str)
              attributes = [str]
            else
              attributes = str.split(/_and_|_or_/)
            end

View on GitHub (pinned to e82f6bab39)

Solutions

  1. Append a valid predicate suffix to the attribute key: name_cont, created_at_gteq, etc. (see Ransack::Translate or the wiki predicate list)
  2. If the predicate should exist, register it: Ransack.configure { |c| c.add_predicate(:my_pred, arel_predicate: 'eq') }
  3. If junk params are expected, use the non-strict Person.ransack(params[:q]) which ignores unknown conditions, or set Ransack.configure { |c| c.ignore_unknown_conditions = true }
  4. Rescue Ransack::InvalidSearchError in the controller and fall back to an unfiltered result for malformed user input

Example fix

# before
Person.ransack!(name: 'Alice')
# => Ransack::InvalidSearchError: No valid predicate for name

# after
Person.ransack!(name_cont: 'Alice')
Defensive patterns

Strategy: try-catch

Validate before calling

KNOWN_PREDICATES = %w[eq not_eq cont start end gt lt gteq lteq in not_in null not_null true false present blank matches does_not_match].freeze

def sanitized_ransack_params(raw)
  raw.select do |key, _|
    key == 's' || key == 'sorts' ||
      key.to_s.split(/_(or|and)_/).all? { |part|
        part.match?(/_(#{KNOWN_PREDICATES.join('|')})$/)
      }
  end
end

Person.ransack!(sanitized_ransack_params(params[:q]))

Type guard

def valid_condition_key?(key)
  Ransack::Predicate.named(
    Ransack::Predicate.detect_and_strip_from_string!(key.dup)
  ).present?
end

Try / catch

begin
  @q = Person.ransack!(params[:q])
  @people = @q.result
rescue Ransack::InvalidSearchError => e
  flash.now[:alert] = 'Invalid search filters; showing all records.'
  Rails.logger.info("Bad ransack params: #{e.message}")
  @q = Person.ransack
  @people = Person.all
end

Prevention

When it happens

Trigger: Person.ransack!('name' => 'x') — key lacks a predicate suffix; a typo'd predicate like name_conct or value_gte (missing q); a custom predicate used in the form but never registered via Ransack.configure { config.add_predicate }; URL params crafted or truncated by hand (q[c][0][a]=name with no _eq).

Common situations: Using strict ransack! in controllers to surface bad params; renaming/removing a custom predicate while view forms still submit the old suffix; hand-built search URLs missing suffixes; Ransack upgrades where a predicate name changed.

Related errors


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