{"record":{"id":"ac84e0afc3ae67d9","repo":"thoughtbot/factory_bot","slug":"self-referencing-association-attribute-name-i","errorCode":null,"errorMessage":"Self-referencing association '#{attribute.name}' in '#{attribute.factory}'","messagePattern":"Self-referencing association '#(.+?)' in '#(.+?)'","errorType":"exception","errorClass":"FactoryBot::AssociationDefinitionError","httpStatus":null,"severity":"error","filePath":"lib/factory_bot/attribute_list.rb","lineNumber":58,"sourceCode":"    end\n\n    private\n\n    def add_attribute(attribute)\n      @attributes << attribute\n      attribute\n    end\n\n    def ensure_attribute_not_defined!(attribute)\n      if attribute_defined?(attribute.name)\n        raise AttributeDefinitionError, \"Attribute already defined: #{attribute.name}\"\n      end\n    end\n\n    def ensure_attribute_not_self_referencing!(attribute)\n      if attribute.respond_to?(:factory) && attribute.factory == @name\n        message = \"Self-referencing association '#{attribute.name}' in '#{attribute.factory}'\"\n        raise AssociationDefinitionError, message\n      end\n    end\n\n    def attribute_defined?(attribute_name)\n      @attributes.any? do |attribute|\n        attribute.name == attribute_name\n      end\n    end\n  end\nend\n","sourceCodeStart":40,"sourceCodeEnd":69,"githubUrl":"https://github.com/thoughtbot/factory_bot/blob/18ae8b581bf55de681c8adb5f74d32787fd2157f/lib/factory_bot/attribute_list.rb#L40-L69","documentation":"AssociationDefinitionError raised by AttributeList#ensure_attribute_not_self_referencing! (attribute_list.rb:55-60) when an association declared directly in a factory's body targets that same factory (attribute.factory == the list's name, which is the factory's name). Examples: `association :parent, factory: :comment` inside `factory :comment`, or implicit `association :author` inside `factory :author`. The guard prevents infinite recursion when the declaration is evaluated.","triggerScenarios":"Modeling tree structures: `FactoryBot.define { factory :comment do association :parent, factory: :comment end }`, or `factory :author do association :author end` where the attribute name implicitly selects the same factory. Raises when declarations are compiled, i.e. on the first build/create/lint.","commonSituations":"Self-referential ActiveRecord models (comments, categories, org charts, referral trees) with `belongs_to :parent, class_name: 'Comment'`; porting fixtures that used nil parents; the first attempt at a factory for a recursive model almost always hits this guard.","solutions":["Use the dynamic block form, evaluated at build time and not subject to the declaration check: `parent { nil }` or `parent { association(:comment) }`, then override per test.","Move the association into a trait (`trait :with_parent do association :parent, factory: :comment end`) — a trait's attribute list is named after the trait, so the guard does not fire.","Create a child factory: `factory :comment_with_parent, parent: :comment do association :parent, factory: :comment end`.","Or set the parent explicitly at call time: `create(:comment, parent: create(:comment))`."],"exampleFix":"# before\nFactoryBot.define do\n  factory :comment do\n    body { 'hi' }\n    association :parent, factory: :comment # self-referencing -> raises\n  end\nend\n\n# after\nFactoryBot.define do\n  factory :comment do\n    body { 'hi' }\n    parent { nil }\n\n    trait :with_parent do\n      parent { association(:comment) } # block form evaluates at build time\n    end\n  end\nend","handlingStrategy":"validation","validationCode":"# refuse same-factory associations up front\ndef safe_association(factory_name, name, *options)\n  opts = options.extract_options!\n  target = opts.fetch(:factory, name)\n  if target.to_s == factory_name.to_s\n    raise FactoryBot::AssociationDefinitionError, \"#{name} would self-reference :#{factory_name}\"\n  end\n  association(name, *options)\nend","typeGuard":"->(target, own_name) { target.to_s != own_name.to_s }","tryCatchPattern":"begin\n  FactoryBot.build(:comment)\nrescue FactoryBot::AssociationDefinitionError => e\n  raise unless e.message.include?('Self-referencing association')\n  # switch to `parent { association(:comment) }` (block form) or a :with_parent trait\nend","preventionTips":["For self-referential models default the parent to nil and add a :with_parent trait using the block form.","Never point `factory:` at the factory currently being defined in its own body.","Run FactoryBot.lint(traits: true) in CI so self-references are caught before runtime."],"tags":["ruby","factory-bot","association","self-reference","recursion"],"backgroundTag":"circular-reference","analyzedSha":"18ae8b581bf55de681c8adb5f74d32787fd2157f","analyzedAt":"2026-08-21T18:25:33.545Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}