activerecord-hackery/ransack · error · TypeError

First argument must be a Ransack::Search!

Error message

First argument must be a Ransack::Search!

What it means

The sort_link view helper raises a TypeError when, after unwrapping an optional [routing_proxy, search] array, its first effective argument is not a Ransack::Search instance. sort_link reads the search's context (klass, sorts) to build the column-header link, so any other object (model instance, relation, nil) is rejected up front.

Source

Thrown at lib/ransack/helpers/form_helper.rb:68

        html_options = build_html_options(search, options, method).merge(turbo_options)
        finalize_form_options(options, html_options)
        form_for(record, options, &proc)
      end

      # +sort_link+
      #
      #   <%= sort_link(@q, :name, [:name, 'kind ASC'], 'Player Name') %>
      #
      #   You can also use a block:
      #
      #   <%= sort_link(@q, :name, [:name, 'kind ASC']) do %>
      #     <strong>Player Name</strong>
      #   <% end %>
      #
      def sort_link(search_object, attribute, *args, &block)
        search, routing_proxy = extract_search_and_routing_proxy(search_object)
        unless Search === search
          raise TypeError, 'First argument must be a Ransack::Search!'
        end
        args[args.first.is_a?(Array) ? 1 : 0, 0] = capture(&block) if block_given?
        s = SortLink.new(search, attribute, args, params, &block)
        link_to(s.name, url(routing_proxy, s.url_options), s.html_options(args))
      end

      # +sort_url+
      # <%= sort_url(@q, :created_at, default_order: :desc) %>
      #
      def sort_url(search_object, attribute, *args)
        search, routing_proxy = extract_search_and_routing_proxy(search_object)
        unless Search === search
          raise TypeError, 'First argument must be a Ransack::Search!'
        end
        s = SortLink.new(search, attribute, args, params)
        url(routing_proxy, s.url_options)
      end

View on GitHub (pinned to e82f6bab39)

Solutions

  1. Set @q = Person.ransack(params[:q]) in the controller and call sort_link(@q, :name)
  2. If you use the array form for routing proxies, keep the search object as the second element: sort_link([:admin, @q], :name)
  3. If @q can be nil (e.g. non-GET renders), initialize a default: @q ||= Person.ransack

Example fix

# controller
# before
def index
  @people = Person.all
end

# after
def index
  @q = Person.ransack(params[:q])
  @people = @q.result
end

# view (unchanged)
<%= sort_link(@q, :name, 'Person Name') %>
Defensive patterns

Strategy: type-guard

Validate before calling

# in the controller, guarantee the instance var exists before render:
@q = Person.ransack(params[:q]) if @q.nil?

Type guard

def ransack_search?(obj)
  obj.is_a?(Ransack::Search)
end

# view:
<%= sort_link(@q, :name) if ransack_search?(@q) %>

Prevention

When it happens

Trigger: sort_link(@person, :name) where @person is a record or relation; sort_link(params[:q], :name) (raw params hash); forgetting @q = Person.ransack(params[:q]) in the controller; passing a routing-proxy array whose second element is not the search, e.g. [admin_proxy, @person].

Common situations: New Ransack installations where the controller still passes a relation to the view; refactoring views and dropping the @q assignment; using sort_link in a partial that receives the wrong local variable; passing an array form incorrectly for engines/admin namespaces.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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