instructure/canvas-lms · error · ArgumentError

cannot pass both a module_name and a block

Error message

cannot pass both a module_name and a block

What it means

Autoextend.hook is strictly either/or: you may supply a module_name (existing module to mix in) OR a &block (inline module), never both. Supplying both is ambiguous — the gem would not know which extension source to use — so hook raises ArgumentError up front.

Solutions

  1. Remove the block and keep module_name if the extension lives in an existing module
  2. Remove the module_name argument and keep the block for an inline extension
  3. Split into two separate hook calls if both extensions are genuinely needed

Example fix

// before
Autoextend.hook('User', 'UserExtensions', :create) { def extra; end }
// after
Autoextend.hook('User', 'UserExtensions', :create)
Defensive patterns

Strategy: validation

Validate before calling

raise 'pass module_name XOR block' if mod_name && block_given?

Try / catch

begin
  Autoextend.hook(const_name, mod_name, method, &blk)
rescue ArgumentError => e
  Rails.logger.error("ambiguous autoextend hook: #{e.message}")
end

Prevention

When it happens

Trigger: Calling Autoextend.hook('Model', 'SomeModule', :method) { ... } — a module_name string plus an attached block in the same call.

Common situations: Merging two hook registrations into one call during a refactor; copy-pasting a block-based hook and leaving the old module_name argument in place; IDE snippets that prefill module_name while the developer also writes a block.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/f75a5aa167a36cb9. Report an issue: GitHub.

Appendix: source

Thrown at gems/autoextend/lib/autoextend.rb:66

    # If User is already defined, it will immediately include
    # the MyUserExtension module into it. It then sets up a hook
    # to automatically include in User if it becomes defined again
    # (like from ActiveSupport reloading).
    #
    # You can make an extension optional, which is for information only,
    # so that if you have a spec that checks if all extensions were used
    # it can ignore optional extensions.
    def hook(const_name,
             module_name = nil,
             method: :include,
             singleton: false,
             after_load: false,
             optional: false,
             before: [],
             after: [],
             &block)
      raise ArgumentError, "block is required if module_name is not passed" if !module_name && !block
      raise ArgumentError, "cannot pass both a module_name and a block" if module_name && block

      extension = Extension.new(const_name,
                                module_name,
                                method,
                                block,
                                singleton,
                                optional,
                                Array(before),
                                Array(after))

      const_extensions = extensions_hash[const_name.to_sym] ||= []
      const_extensions << extension

      if module_name.is_a?(Module)
        module_name = module_name.name
      end

      # immediately extend the class if it's already defined

View on GitHub (pinned to 1c9f0bb801)