activerecord-hackery/ransack · error · ArgumentError

Don't know how to klassify #{obj.inspect}

Error message

Don't know how to klassify #{obj.inspect}

What it means

The base Ransack::Context#klassify (unlike the ActiveRecord adapter version, which also accepts objects responding to base_klass) raises this ArgumentError when given an object that is neither an ActiveRecord::Base subclass nor responds to #klass. It is reached via Context#traverse while resolving an association/attribute path string to a model class during search building.

Source

Thrown at lib/ransack/context.rb:65

      @base = @join_dependency.instance_variable_get(:@join_root)
    end

    def bind_pair_for(key)
      @bind_pairs ||= {}

      @bind_pairs[key] ||= begin
        parent, attr_name = get_parent_and_attribute_name(key.to_s)
        [parent, attr_name] if parent && attr_name
      end
    end

    def klassify(obj)
      if Class === obj && ::ActiveRecord::Base > obj
        obj
      elsif obj.respond_to? :klass
        obj.klass
      else
        raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
      end
    end

    # Convert a string representing a chain of associations and an attribute
    # into the attribute itself
    def contextualize(str)
      parent, attr_name = bind_pair_for(str)
      table_for(parent)[attr_name]
    end

    def chain_scope(scope, args)
      return unless @klass.method(scope) && args != false
      @object = if scope_arity(scope) < 1 && args == true
                  @object.public_send(scope)
                elsif scope_arity(scope) == 1 && args.is_a?(Array)
                  # For scopes with arity 1, pass the array as a single argument instead of splatting
                  @object.public_send(scope, args)
                else

View on GitHub (pinned to e82f6bab39)

Solutions

  1. Ensure every association in the searched chain is a standard ActiveRecord association pointing at an AR model
  2. If you maintain a custom adapter, implement klassify to handle your join objects (mirror the base_klass fallback the AR adapter has)
  3. Update ransack to a version matching your Rails version — join node internals (klass vs base_klass) shifted across Rails 5.x/6.x/7.x
Defensive patterns

Strategy: validation

Validate before calling

# before calling traverse/klassify paths with a custom base:
def klassifiable_base?(obj)
  (obj.is_a?(Class) && obj < ActiveRecord::Base) || obj.respond_to?(:klass)
end

raise ArgumentError, "cannot traverse from #{obj.inspect}" unless klassifiable_base?(obj)

Type guard

def klassifiable?(obj)
  (obj.is_a?(Class) && obj < ActiveRecord::Base) || obj.respond_to?(:klass)
end

Try / catch

begin
  context.traverse(assoc_path)
rescue ArgumentError => e
  Rails.logger.warn("Skipping untraversable path #{assoc_path}: #{e.message}")
  nil
end

Prevention

When it happens

Trigger: Ransack resolves a path like 'parent_children_grandchildren_attr' and an intermediate segment resolves to an object without #klass — e.g. a join dependency node from a mismatched Rails version, or a custom object injected into the traversal base. Also reachable by calling context.klassify(obj) directly with a plain object.

Common situations: Rails version incompatibilities where JoinDependency internals no longer expose klass on join nodes (the AR adapter's base_klass branch exists precisely for this); custom Ransack adapters; searching associations backed by non-AR classes.

Related errors


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