thoughtbot/factory_bot · error · ArgumentError

Association '#{name}' received an invalid factory argument.

Error message

Association '#{name}' received an invalid factory argument.
Did you mean? 'factory: :#{factory_name.name}'

What it means

Raised by factory_bot when an association's factory option is not a factory name but an implicit Declaration object. Writing `author factory: user` (bare user, no colon) makes DefinitionProxy#method_missing treat `user` as an implicit attribute reference, and that Declaration::Implicit is stored as the factory argument of the association declaration. The guard raise_if_arguments_are_declarations! (association.rb:39-45) detects it when declarations are built, so the error surfaces on the first build/create/lint of the factory, not at definition time.

Source

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

      private

      attr_reader :factory_name, :overrides, :traits

      def build
        raise_if_arguments_are_declarations!

        [
          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 factory target: `association :author, factory: :user` (or the short `author factory: :user`).
  2. If the attribute name already matches the target factory, drop options entirely: `author` implicitly uses the :author factory.
  3. Audit the rest of the options hash — the same typo pattern also triggers the sibling 'invalid attribute override' error.
  4. Add FactoryBot.lint(traits: true) to CI so declaration errors surface deterministically instead of at first use.

Example fix

# before
FactoryBot.define do
  factory :post do
    author factory: user   # bare user becomes a Declaration, not :user
  end
end

# after
FactoryBot.define do
  factory :post do
    author factory: :user
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# before defining, verify association factory targets are symbols/strings
options = { factory: :user }
bad = options.reject { |_, v| v.is_a?(Symbol) || v.is_a?(String) }
raise ArgumentError, "non-symbol association option values: #{bad.keys}" unless bad.empty?

Type guard

->(v) { v.is_a?(Symbol) || v.is_a?(String) }

Try / catch

begin
  FactoryBot.build(:post)
rescue ArgumentError => e
  raise unless e.message.include?('invalid factory argument')
  # a factory option lost its ':' — symbolize it in the association declaration
end

Prevention

When it happens

Trigger: Define `FactoryBot.define { factory :post do author factory: user end }` (missing the colon before user), then call FactoryBot.build(:post). The bare method call inside the options hash produces a Declaration that becomes factory_name; Association#build raises ArgumentError "Association 'author' received an invalid factory argument. Did you mean? 'factory: :user'".

Common situations: Hand-typing association options and dropping the colon; copy-paste from docs or older examples where the leading colon is easy to lose; refactors that turn symbol values into bare references. Often first seen in CI because definition files only fail once a factory is actually built or linted.

Related errors


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