teamcapybara/capybara · error · Capybara::ScopeError
`switch_to_window` is not supposed to be invoked from `withi
Error message
`switch_to_window` is not supposed to be invoked from `within` or `within_frame` blocks.
What it means
switch_to_window permanently changes which window subsequent finders query, so Capybara forbids it while a within or within_frame scope is active (scopes.last non-nil) and raises Capybara::ScopeError naming those blocks as the cause.
Source
Thrown at lib/capybara/session.rb:517
# 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 }
# @param window [Capybara::Window] instance of {Capybara::Window} class
# that will be switched to
# @raise [driver#no_such_window_error] if nonexistent (e.g. closed) window was passed
# @overload within_window(proc_or_lambda) { do_something }View on GitHub (pinned to 15b5fdb76e)
Solutions
- Move the switch outside the within block, then re-scope: switch_to_window(w); within('.modal') { ... }
- Use within_window(w) { ... } for temporary window scoping - it restores the original window on exit
- Restructure page objects so window switches happen at top level, never inside scoped helpers
Example fix
# before
within('.modal') do
switch_to_window(chat_window) # Capybara::ScopeError
end
# after
switch_to_window(chat_window)
within('.modal') { click_button 'Send' } Defensive patterns
Strategy: validation
Validate before calling
switch_to_window(chat_window) if page.send(:scopes).last.nil?
Try / catch
begin
switch_to_window(w)
rescue Capybara::ScopeError
within_window(w) { yield } # temporary scope instead of a permanent switch
end Prevention
- Use within_window for scoped cross-window work
- Keep window switching at the top level of specs and page objects
When it happens
Trigger: within('.modal') { switch_to_window(chat_window) }; within_frame { switch_to_window { title == 'Chat' } }; page-object methods called from inside a scoped block that switch windows.
Common situations: Popups triggered from scoped sections (clicking a button within a modal opens a new window); feature flows mixing within with window management; helpers that switch windows reused from scoped contexts.
Related errors
- Window cannot be switched inside a `within_frame` block
- Window cannot be switched inside a `within` block
- `switch_to_frame(:parent)` cannot be called from inside a de
- `switch_to_frame(:top)` cannot be called from inside a desce
- `switch_to_window` can take either a block or a window, not
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/4ba8a199adc8e96d.
Report an issue: GitHub.