teamcapybara/capybara · error · Capybara::ScopeError

`switch_to_frame(:parent)` cannot be called from inside a de

Error message

`switch_to_frame(:parent)` cannot be called from inside a descendant frame's `within` block.

What it means

switch_to_frame(:parent) is only valid when the innermost scope is a frame (scopes.last == :frame). Calling it while a within block is active - even one nested inside a frame - raises Capybara::ScopeError, because popping to the parent frame would silently escape the element scope the block established.

Source

Thrown at lib/capybara/session.rb:415

    # If you use this method you are responsible for making sure you switch back to the parent frame when done in the frame changed to.
    # {#within_frame} is preferred over this method and should be used when possible.
    # May not be supported by all drivers.
    #
    # @overload switch_to_frame(element)
    #   @param [Capybara::Node::Element] element    iframe/frame element to switch to
    # @overload switch_to_frame(location)
    #   @param [Symbol] location relative location of the frame to switch to
    #                            * :parent - the parent frame
    #                            * :top - the top level document
    #
    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

View on GitHub (pinned to 15b5fdb76e)

Solutions

  1. Close the enclosing within block before calling switch_to_frame(:parent)
  2. Prefer the within_frame(el) { ... } block form, which returns to the parent frame automatically at block exit
  3. To escape all frames, exit every within block first and use switch_to_frame(:top)

Example fix

# before
within_frame(:css, 'iframe.chat') do
  within('.composer') do
    switch_to_frame(:parent) # Capybara::ScopeError
  end
end

# after
within_frame(:css, 'iframe.chat') do
  within('.composer') { fill_in 'Message', with: 'hi' }
end # frame scope restored automatically, no manual switch needed
Defensive patterns

Strategy: validation

Validate before calling

switch_to_frame(:parent) if page.send(:scopes).last == :frame

Try / catch

begin
  switch_to_frame(:parent)
rescue Capybara::ScopeError => e
  raise "still inside a `within` block - restructure before popping frames (#{e.message})"
end

Prevention

When it happens

Trigger: within_frame(el) { within('.toolbar') { switch_to_frame(:parent) } } - the innermost scope is the within scope, not :frame; also any manual switch_to_frame(:parent) issued from page-object methods invoked inside a within block.

Common situations: Page objects mixing within with manual frame navigation; refactoring from the looser Capybara 1.x frame API; copy-pasting frame-navigation helpers into scoped contexts.

Related errors


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