thoughtbot/factory_bot · error · FactoryBot::TraitDefinitionError

Self-referencing trait '#{@name}'

Error message

Self-referencing trait '#{@name}'

What it means

TraitDefinitionError raised by Declaration::Implicit#build (implicit.rb:23-34) when a bare name inside a trait (or factory) body resolves to nothing registered — not a factory, not a sequence — and equals the enclosing definition's own name. The bare word would make the trait include itself, so factory_bot raises 'Self-referencing trait'. The message says 'trait' even when the same happens in a plain factory body referencing its own name.

Source

Thrown at lib/factory_bot/declaration/implicit.rb:30

          name == other.name &&
          factory == other.factory &&
          ignored == other.ignored
      end

      protected

      attr_reader :factory

      private

      def build
        if FactoryBot.factories.registered?(name)
          [Attribute::Association.new(name, name, {})]
        elsif FactoryBot::Internal.sequences.registered?(name)
          [Attribute::Sequence.new(name, name, @ignored)]
        elsif @factory.name.to_s == name.to_s
          message = "Self-referencing trait '#{@name}'"
          raise TraitDefinitionError, message
        else
          @factory.inherit_traits([name])
          []
        end
      end
    end
  end
end

View on GitHub (pinned to 18ae8b581b)

Solutions

  1. Give the attribute a value with a block: `trait :admin do admin { true } end`.
  2. Or declare it explicitly: `add_attribute(:admin) { true }`.
  3. If the trait should compose other traits, reference those by name — never itself.
  4. If the goal was an association to a like-named factory, use `association :admin, factory: :admin_user` with an explicit, different factory target.

Example fix

# before
FactoryBot.define do
  factory :user do
    trait :admin do
      admin   # bare self-reference -> Self-referencing trait 'admin'
    end
  end
end

# after
FactoryBot.define do
  factory :user do
    trait :admin do
      admin { true }
    end
  end
end
Defensive patterns

Strategy: validation

Try / catch

begin
  FactoryBot.build(:user, :admin)
rescue FactoryBot::TraitDefinitionError => e
  raise unless e.message.include?('Self-referencing trait')
  # give the trait body a block: admin { true }
end

Prevention

When it happens

Trigger: Define `trait :admin do admin end` inside `factory :user`, then build with the trait: `FactoryBot.build(:user, :admin)`. The implicit `admin` is not a registered factory or sequence, and the trait's definition name equals 'admin', so TraitDefinitionError 'Self-referencing trait \'admin\'' is raised when the trait is compiled. A factory body referencing its own not-yet-registered name (`factory :user do user end`) hits the same branch.

Common situations: Writing a trait intended to set a like-named boolean attribute (`admin`, `active`, `verified`) — `trait :active do active end` reads naturally as 'set active' but is a self-include; renaming traits to mirror attribute names; porting older trait definitions that relied on implicit resolution.

Related errors


AI-assisted analysis of thoughtbot/factory_bot@18ae8b581b (2026-08-21). Data as JSON: /api/errors/2b8c0915f82dde9c. Report an issue: GitHub.