activerecord-hackery/ransack · error · ArgumentError

sort_direction must be called inside a search FormBuilder!

Error message

sort_direction must be called inside a search FormBuilder!

What it means

Ransack's FormBuilder#sort_direction_select (called directly or via sort_select/sort_fields) raises this ArgumentError when the form builder's object does not respond to #context — i.e. the form was not built around a Ransack::Search object. The guard exists because the helper reads sortable attributes from the search's context to populate the asc/desc dropdown.

Source

Thrown at lib/ransack/helpers/form_builder.rb:72

        bases = [''.freeze].freeze + association_array(options[:associations])
        if bases.size > 1
          collection = attribute_collection_for_bases(action, bases)
          object.name ||= default if can_use_default?(
            default, :name, mapped_values(collection.flatten(2))
            )
          template_grouped_collection_select(collection, options, html_options)
        else
          collection = collection_for_base(action, bases.first)
          object.name ||= default if can_use_default?(
            default, :name, mapped_values(collection)
            )
          template_collection_select(:name, collection, options, html_options)
        end
      end

      def sort_direction_select(options = {}, html_options = {})
        unless object.respond_to?(:context)
          raise ArgumentError,
          formbuilder_error_message('sort_direction'.freeze)
        end
        template_collection_select(:dir, sort_array, options, html_options)
      end

      def sort_select(options = {}, html_options = {})
        attribute_select(options, html_options, 'sort'.freeze) +
        sort_direction_select(options, html_options)
      end

      def sort_fields(*args, &block)
        search_fields(:s, args, block)
      end

      def sort_link(attribute, *args)
        @template.sort_link @object, attribute, *args
      end

View on GitHub (pinned to e82f6bab39)

Solutions

  1. Build @q = Person.ransack(params[:q]) in the controller
  2. Render the form with search_form_for @q so the builder binds to the search object
  3. For plain forms, hand-roll the direction select: f.select :dir, [['asc', 'asc'], ['desc', 'desc']]

Example fix

<!-- before -->
<%= form_for @person do |f| %>
  <%= f.sort_direction_select %>
<% end %>

<!-- after -->
<%= search_form_for @q do |f| %>
  <%= f.sort_direction_select %>
<% end %>
Defensive patterns

Strategy: type-guard

Type guard

def search_bound_form?(builder)
  builder.object.respond_to?(:context)
end

Prevention

When it happens

Trigger: <%= form_for @person do |f| %> <%= f.sort_direction_select %> or f.sort_select inside a plain model-bound form; also when a custom builder wraps Ransack's FormBuilder but the object passed in is a model instance.

Common situations: Reusing sort-select snippets from Ransack docs inside existing simple_form/form_with forms; forgetting @q = Model.ransack(params[:q]) in the controller action that renders the form.

Related errors


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