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
- Give the attribute a value with a block: `trait :admin do admin { true } end`.
- Or declare it explicitly: `add_attribute(:admin) { true }`.
- If the trait should compose other traits, reference those by name — never itself.
- 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 a trait shares its name with an attribute, always assign a value in a block (`admin { true }`).
- Never reference the trait's own name bare inside its body.
- Run FactoryBot.lint(traits: true) so bad traits fail in CI with the trait name in the message.
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
- Self-referencing association '#{attribute.name}' in '#{attri
- Association '#{name}' received an invalid factory argument.
- Association '#{name}' received an invalid attribute override
- undefined method '#{name}' in '#{@definition.name}' factory
- Unexpected block passed to '#{name}' association in '#{@defi
AI-assisted analysis of thoughtbot/factory_bot@18ae8b581b (2026-08-21).
Data as JSON: /api/errors/2b8c0915f82dde9c.
Report an issue: GitHub.