thoughtbot/factory_bot · error · ArgumentError

Association '#{name}' received an invalid attribute override

Error message

Association '#{name}' received an invalid attribute override.
Did you mean? '#{attribute}: :#{value.name}'

What it means

Raised by factory_bot when a value inside an association's options hash is an implicit Declaration instead of a literal. With `author factory: :user, invalid_attribute: implicit_trait`, the bare `implicit_trait` is dispatched through DefinitionProxy#method_missing and becomes a Declaration::Implicit stored as the override value; raise_if_arguments_are_declarations! (association.rb:47-54) catches it when the declaration list is built (first build/lint) and suggests the symbol form.

Source

Thrown at lib/factory_bot/declaration/association.rb:49

          Attribute::Association.new(
            name,
            factory_name,
            [traits, overrides].flatten
          )
        ]
      end

      def raise_if_arguments_are_declarations!
        if factory_name.is_a?(Declaration)
          raise ArgumentError.new(<<~MSG)
            Association '#{name}' received an invalid factory argument.
            Did you mean? 'factory: :#{factory_name.name}'
          MSG
        end

        overrides.each do |attribute, value|
          if value.is_a?(Declaration)
            raise ArgumentError.new(<<~MSG)
              Association '#{name}' received an invalid attribute override.
              Did you mean? '#{attribute}: :#{value.name}'
            MSG
          end
        end
      end
    end
  end
end

View on GitHub (pinned to 18ae8b581b)

Solutions

  1. Symbolize the override value: `association :author, factory: :user, invalid_attribute: :implicit_trait`.
  2. If the value is not meant to be a factory/trait/sequence name, use a literal (string, integer, boolean) or a block-computed attribute on the parent factory instead.
  3. Check every key in the same hash — one bare reference usually means more are nearby.
  4. Run FactoryBot.lint(traits: true) in CI to catch all declaration mistakes at build time.

Example fix

# before
FactoryBot.define do
  factory :post do
    author factory: :user, invalid_attribute: implicit_trait
  end
end

# after
FactoryBot.define do
  factory :post do
    association :author, factory: :user, invalid_attribute: :implicit_trait
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# verify every override value in association options is a literal, not a bare reference
options = { factory: :user, invalid_attribute: :implicit_trait }
bad = options.reject { |_, v| [Symbol, String, Integer, Float, TrueClass, FalseClass, NilClass].any? { |t| v.is_a?(t) } }
raise ArgumentError, "suspicious override values: #{bad.keys}" unless bad.empty?

Type guard

->(v) { v.is_a?(Symbol) || v.is_a?(String) || v.is_a?(Numeric) || [true, false, nil].include?(v) }

Try / catch

begin
  FactoryBot.build(:post)
rescue ArgumentError => e
  raise unless e.message.include?('invalid attribute override')
  # an override value lost its ':' — symbolize it in the association options
end

Prevention

When it happens

Trigger: Define `FactoryBot.define { factory :post do author factory: :user, invalid_attribute: implicit_trait end }` and run FactoryBot.build(:post). Any non-literal, non-symbol value in the association options hash that resolves to a Declaration (bare name, no colon) triggers "Association 'author' received an invalid attribute override. Did you mean? 'invalid_attribute: :implicit_trait'".

Common situations: Writing association overrides the same way as implicit trait references inside factory bodies; dropping colons when hand-writing option hashes; refactoring a working association line and leaving a bare reference behind; errors appear only when the factory is first built, so they surface late in CI.

Related errors


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