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
endView on GitHub (pinned to 8c1bf153a3)
Solutions
- Check the real association name in console (Comment.reflect_on_all_associations) and use it in the conditions hash.
- Add the missing association to the model if the relation should exist (belongs_to :post).
- 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
- Derive nested condition keys from reflect_on_all_associations, not from memory.
- Add an Ability lint spec that reflects every conditions-hash key against its model in CI.
- For plain FK columns use flat conditions (owner_id: x), not pseudo-association keys.
- Update ability.rb in the same commit whenever an association is renamed.
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
- You are not authorized to access this page.
- You are not authorized to access this page.
- The accessible_by call cannot be used with a block 'can' def
- This action failed the check_authorization because it does n
- You can't specify target (#{target}) as alias because it is
AI-assisted analysis of CanCanCommunity/cancancan@8c1bf153a3 (2026-08-21).
Data as JSON: /api/errors/89ddbc7ca0e55418.
Report an issue: GitHub.