CanCanCommunity/cancancan · error · CanCan::WrongAssociationName

Association '#{key}' not defined in model '#{model_class.nam

Error message

Association '#{key}' not defined in model '#{model_class.name}'

What it means

When normalizing nested conditions, ConditionsNormalizer#calculate_result_hash (lib/cancan/model_adapters/conditions_normalizer.rb:31) calls model_class.reflect_on_association(key) for every hash key. If the key is not an association defined on that model, it raises CanCan::WrongAssociationName with the offending key and model name. This almost always means the conditions hash names an association that does not exist (typo, or the FK is a plain column, not an association).

Source

Thrown at lib/cancan/model_adapters/conditions_normalizer.rb:31

        def normalize_conditions(model_class, conditions)
          return conditions unless conditions.is_a? Hash

          conditions.each_with_object({}) do |(key, value), result_hash|
            if value.is_a? Hash
              result_hash.merge!(calculate_result_hash(model_class, key, value))
            else
              result_hash[key] = value
            end
            result_hash
          end
        end

        private

        def calculate_result_hash(model_class, key, value)
          reflection = model_class.reflect_on_association(key)
          unless reflection
            raise WrongAssociationName, "Association '#{key}' not defined in model '#{model_class.name}'"
          end

          if normalizable_association? reflection
            key = reflection.options[:through]
            value = { reflection.source_reflection_name => value }
            reflection = model_class.reflect_on_association(key)
          end

          { key => normalize_conditions(reflection.klass.name.constantize, value) }
        end

        def normalizable_association?(reflection)
          reflection.options[:through].present? && !reflection.options[:source_type].present?
        end
      end
    end
  end
end

View on GitHub (pinned to 8c1bf153a3)

Solutions

  1. Check the real association name in console (Comment.reflect_on_all_associations) and use it in the conditions hash.
  2. Add the missing association to the model if the relation should exist (belongs_to :post).
  3. For plain columns, use flat conditions on the model itself: can :read, Task, owner_id: 1 instead of owner: 1.

Example fix

# before
class Comment < ApplicationRecord
  belongs_to :author, class_name: 'User'
end
can :read, Comment, user: { admin: true }  # no :user association -> WrongAssociationName

# after
can :read, Comment, author: { admin: true }
Defensive patterns

Strategy: validation

Validate before calling

# validate ability conditions against real associations before defining
conditions = { project: { owner_id: user.id } }
conditions.each_key do |key|
  raise CanCan::WrongAssociationName, "#{key} is not an association of Comment" unless Comment.reflect_on_association(key)
end
can :read, Comment, **conditions

Type guard

def association_condition?(model_class, key)
  model_class.reflect_on_association(key).present?
end

Prevention

When it happens

Trigger: can :read, Comment, post: { user_id: 1 } when Comment belongs_to :author (no :post association); a key that is actually a plain column (can :read, Task, owner: 1 when Task has owner_id but no belongs_to :owner); through-associations whose intermediate name is misspelled.

Common situations: Writing nested ability conditions from memory instead of the schema; renaming associations in a refactor without updating ability.rb; using column names where an association of the same name doesn't exist.

Related errors


AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21). Data as JSON: /api/errors/89ddbc7ca0e55418. Report an issue: GitHub.