teamcapybara/capybara · error · Capybara::ScopeError
`switch_to_frame(:top)` cannot be called from inside a desce
Error message
`switch_to_frame(:top)` cannot be called from inside a descendant frame's `within` block.
What it means
switch_to_frame(:top) resets to the top-level document, but only when every scope from the outermost frame onward is a frame or nil. If a within scope sits inside the frame chain, the slice check fails and Capybara::ScopeError is raised rather than silently discarding that element scope.
Source
Thrown at lib/capybara/session.rb:425
#
def switch_to_frame(frame)
case frame
when Capybara::Node::Element
driver.switch_to_frame(frame)
scopes.push(:frame)
when :parent
if scopes.last != :frame
raise Capybara::ScopeError, "`switch_to_frame(:parent)` cannot be called from inside a descendant frame's " \
'`within` block.'
end
scopes.pop
driver.switch_to_frame(:parent)
when :top
idx = scopes.index(:frame)
top_level_scopes = [:frame, nil]
if idx
if scopes.slice(idx..).any? { |scope| !top_level_scopes.include?(scope) }
raise Capybara::ScopeError, "`switch_to_frame(:top)` cannot be called from inside a descendant frame's " \
'`within` block.'
end
scopes.slice!(idx..)
driver.switch_to_frame(:top)
end
else
raise ArgumentError, 'You must provide a frame element, :parent, or :top when calling switch_to_frame'
end
end
##
#
# Execute the given block within the given iframe using given frame, frame name/id or index.
# May not be supported by all drivers.
#
# @overload within_frame(element)
# @param [Capybara::Node::Element] frame element
# @overload within_frame([kind = :frame], locator, **options)View on GitHub (pinned to 15b5fdb76e)
Solutions
- Exit the within block (and any nested scoping) before calling switch_to_frame(:top)
- Let within_frame blocks expire naturally - each block exit already returns one level toward the top
- Restructure page objects so top-level resets happen at the top level, not inside scoped helpers
Example fix
# before
within_frame(:css, 'iframe.editor') do
within('.canvas') do
switch_to_frame(:top) # Capybara::ScopeError
end
end
# after
within_frame(:css, 'iframe.editor') do
within('.canvas') { click_button 'Done' }
end # back at the top-level document automatically Defensive patterns
Strategy: validation
Validate before calling
scopes = page.send(:scopes)
idx = scopes.index(:frame)
at_frame_top_only = idx.nil? || scopes.slice(idx..).all? { |s| s.nil? || s == :frame }
switch_to_frame(:top) if at_frame_top_only Try / catch
begin
switch_to_frame(:top)
rescue Capybara::ScopeError => e
raise "a `within` block is open inside the frame - close it first (#{e.message})"
end Prevention
- Avoid manual :top resets inside scoped code; rely on within_frame block exits
- Keep a single owner of frame state per page object
When it happens
Trigger: within_frame(el) { within('.modal') { switch_to_frame(:top) } } - after the :frame scope a within scope remains, so scopes.slice(idx..).any? { |s| ![:frame, nil].include?(s) } trips; likewise calling switch_to_frame(:top) from helpers already running inside a within block.
Common situations: Deeply nested frame + within page objects; attempts to 'escape everything' from inside scoped helpers; refactors that moved switch_to_frame calls into methods already inside within.
Related errors
- `switch_to_frame(:parent)` cannot be called from inside a de
- You must provide a frame element, :parent, or :top when call
- `switch_to_window` is not supposed to be invoked from `withi
- Window cannot be switched inside a `within_frame` block
- Window cannot be switched inside a `within` block
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/cc2345722290224d.
Report an issue: GitHub.