teamcapybara/capybara · error · ArgumentError

`switch_to_window` can take either a block or a window, not

Error message

`switch_to_window` can take either a block or a window, not both

What it means

switch_to_window supports two addressing modes - a Capybara::Window object or a block evaluated per window - and explicitly rejects using both at once with ArgumentError before any driver work happens.

Source

Thrown at lib/capybara/session.rb:513

    # Switch to the given window.
    #
    # @overload switch_to_window(&block)
    #   Switches to the first window for which given block returns a value other than false or nil.
    #   If window that matches block can't be found, the window will be switched back and {Capybara::WindowError} will be raised.
    #   @example
    #     window = switch_to_window { title == 'Page title' }
    #   @raise [Capybara::WindowError]     if no window matches given block
    # @overload switch_to_window(window)
    #   @param window [Capybara::Window]   window that should be switched to
    #   @raise [Capybara::Driver::Base#no_such_window_error] if nonexistent (e.g. closed) window was passed
    #
    # @return [Capybara::Window]         window that has been switched to
    # @raise [Capybara::ScopeError]        if this method is invoked inside {#within} or
    #   {#within_frame} methods
    # @raise [ArgumentError]               if both or neither arguments were provided
    #
    def switch_to_window(window = nil, **options, &window_locator)
      raise ArgumentError, '`switch_to_window` can take either a block or a window, not both' if window && window_locator
      raise ArgumentError, '`switch_to_window`: either window or block should be provided' if !window && !window_locator

      unless scopes.last.nil?
        raise Capybara::ScopeError, '`switch_to_window` is not supposed to be invoked from ' \
                                    '`within` or `within_frame` blocks.'
      end

      _switch_to_window(window, **options, &window_locator)
    end

    ##
    # This method does the following:
    #
    # 1. Switches to the given window (it can be located by window instance/lambda/string).
    # 2. Executes the given block (within window located at previous step).
    # 3. Switches back (this step will be invoked even if an exception occurs at the second step).
    #
    # @overload within_window(window) { do_something }

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Pick one mode: switch_to_window(window) to switch to a known window object
  2. Or switch_to_window { title == 'Chat' } to find and switch by predicate
  3. Audit helper signatures so the window argument is dropped when a block is given

Example fix

# before
switch_to_window(chat_window) { title == 'Chat' }

# after
switch_to_window(chat_window)
# or
switch_to_window { title == 'Chat' }
Defensive patterns

Strategy: type-guard

Validate before calling

raise ArgumentError, 'window or block, not both' if window && block_given?
switch_to_window(window)

Type guard

def window_arg?(obj)
  obj.is_a?(Capybara::Window)
end

switch_to_window(w) if window_arg?(w)

Prevention

When it happens

Trigger: switch_to_window(page.windows.last) { title == 'Chat' } - a window argument plus a locator block in the same call; helper methods that always pass a block while callers also supply a window argument.

Common situations: Refactoring between the two addressing modes and leaving both in place; page-object helpers with a default window parameter invoked together with a block.

Related errors


AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21). Data as JSON: /api/errors/adbef161a4a4e688. Report an issue: GitHub.