{"record":{"id":"89ddbc7ca0e55418","repo":"CanCanCommunity/cancancan","slug":"association-key-not-defined-in-model-model","errorCode":null,"errorMessage":"Association '#{key}' not defined in model '#{model_class.name}'","messagePattern":"Association '#(.+?)' not defined in model '#(.+?)'","errorType":"exception","errorClass":"CanCan::WrongAssociationName","httpStatus":null,"severity":"error","filePath":"lib/cancan/model_adapters/conditions_normalizer.rb","lineNumber":31,"sourceCode":"        def normalize_conditions(model_class, conditions)\n          return conditions unless conditions.is_a? Hash\n\n          conditions.each_with_object({}) do |(key, value), result_hash|\n            if value.is_a? Hash\n              result_hash.merge!(calculate_result_hash(model_class, key, value))\n            else\n              result_hash[key] = value\n            end\n            result_hash\n          end\n        end\n\n        private\n\n        def calculate_result_hash(model_class, key, value)\n          reflection = model_class.reflect_on_association(key)\n          unless reflection\n            raise WrongAssociationName, \"Association '#{key}' not defined in model '#{model_class.name}'\"\n          end\n\n          if normalizable_association? reflection\n            key = reflection.options[:through]\n            value = { reflection.source_reflection_name => value }\n            reflection = model_class.reflect_on_association(key)\n          end\n\n          { key => normalize_conditions(reflection.klass.name.constantize, value) }\n        end\n\n        def normalizable_association?(reflection)\n          reflection.options[:through].present? && !reflection.options[:source_type].present?\n        end\n      end\n    end\n  end\nend","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/CanCanCommunity/cancancan/blob/8c1bf153a3da7b2261d6fa4a5f84eb28e2feb828/lib/cancan/model_adapters/conditions_normalizer.rb#L13-L49","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nclass Comment < ApplicationRecord\n  belongs_to :author, class_name: 'User'\nend\ncan :read, Comment, user: { admin: true }  # no :user association -> WrongAssociationName\n\n# after\ncan :read, Comment, author: { admin: true }","handlingStrategy":"validation","validationCode":"# validate ability conditions against real associations before defining\nconditions = { project: { owner_id: user.id } }\nconditions.each_key do |key|\n  raise CanCan::WrongAssociationName, \"#{key} is not an association of Comment\" unless Comment.reflect_on_association(key)\nend\ncan :read, Comment, **conditions","typeGuard":"def association_condition?(model_class, key)\n  model_class.reflect_on_association(key).present?\nend","tryCatchPattern":null,"preventionTips":["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."],"tags":["conditions-hash","associations","typo","rails","cancancan"],"backgroundTag":"unknown-association-name","analyzedSha":"8c1bf153a3da7b2261d6fa4a5f84eb28e2feb828","analyzedAt":"2026-08-21T20:05:55.000Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}