thoughtbot/factory_bot · error · FactoryBot::AttributeDefinitionError
Attribute already defined: #{attribute.name}
Error message
Attribute already defined: #{attribute.name} What it means
AttributeDefinitionError raised by AttributeList#ensure_attribute_not_defined! (attribute_list.rb:49-53) when two declarations in the same factory body, trait body, or transient block compile to attributes with the same name. Declarations are checked when the attribute list is compiled (first build/create), so the error surfaces lazily. Overriding an attribute inherited from a parent factory is legal — inherited attributes merge via apply_attributes, which bypasses this check.
Source
Thrown at lib/factory_bot/attribute_list.rb:51
def non_transient
AttributeList.new(@name, reject(&:ignored))
end
def apply_attributes(attributes_to_apply)
attributes_to_apply.each { |attribute| add_attribute(attribute) }
end
private
def add_attribute(attribute)
@attributes << attribute
attribute
end
def ensure_attribute_not_defined!(attribute)
if attribute_defined?(attribute.name)
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
end
end
def ensure_attribute_not_self_referencing!(attribute)
if attribute.respond_to?(:factory) && attribute.factory == @name
message = "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
raise AssociationDefinitionError, message
end
end
def attribute_defined?(attribute_name)
@attributes.any? do |attribute|
attribute.name == attribute_name
end
end
end
end
View on GitHub (pinned to 18ae8b581b)
Solutions
- Locate both declarations for the named attribute in that factory/trait/transient body and delete or rename one.
- If one is a sequence and one a block, keep the sequence and remove the explicit attribute (or vice versa).
- Remember same-body duplicates are the problem — redefining an inherited attribute in a child factory is allowed and not the cause.
- Run FactoryBot.lint(traits: true) so duplicates surface in CI with the factory name.
Example fix
# before
FactoryBot.define do
factory :user do
email { 'a@example.com' }
sequence :email { |n| "user#{n}@example.com" } # duplicate :email
end
end
# after
FactoryBot.define do
factory :user do
sequence :email { |n| "user#{n}@example.com" }
end
end Defensive patterns
Strategy: validation
Validate before calling
# after defining, verify no duplicate attribute names before first use
names = FactoryBot::Internal.factory_by_name(:user).definition.declarations.map(&:name)
dups = names.tally.select { |_, c| c > 1 }.keys
raise "duplicate attributes in :user factory: #{dups}" unless dups.empty? Type guard
->(names) { names.size == names.uniq.size } Try / catch
begin FactoryBot.build(:user) rescue FactoryBot::AttributeDefinitionError => e # e.message names the duplicated attribute; remove or rename one of its declarations end
Prevention
- Search the factory file for an attribute name before adding a new declaration for it.
- Never mix `sequence :email` and `email { ... }` in the same factory body.
- Remember parent-factory overrides are allowed; only same-body duplicates raise.
When it happens
Trigger: `factory :user do name { 'A' }; name { 'B' } end`; combining `sequence :email { |n| ... }` with an explicit `email { 'a@b.c' }` in the same body; declaring `email` in both the body and the same transient block (transient shares the definition's declaration list). Raises 'Attribute already defined: name' on first use of the factory.
Common situations: Copy-paste of attribute blocks that leaves two similar lines; renaming an attribute without removing the old declaration; adding a sequence for an attribute still set by a block; merging two factories into one body during refactoring.
Related errors
- 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
- Self-referencing association '#{attribute.name}' in '#{attri
AI-assisted analysis of thoughtbot/factory_bot@18ae8b581b (2026-08-21).
Data as JSON: /api/errors/62b335501ade81e8.
Report an issue: GitHub.