teamcapybara/capybara · error · ArgumentError
You must provide a frame element, :parent, or :top when call
Error message
You must provide a frame element, :parent, or :top when calling switch_to_frame
What it means
switch_to_frame accepts exactly three argument kinds: a Capybara::Node::Element for a located frame, :parent, and :top. Anything else - a String frame name/id, an Integer index, nil - reaches the else branch and raises ArgumentError immediately, before any driver call.
Source
Thrown at lib/capybara/session.rb:432
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)
# @param [Symbol] kind Optional selector type (:frame, :css, :xpath, etc.) - Defaults to :frame
# @param [String] locator The locator for the given selector kind. For :frame this is the name/id of a frame/iframe element
# @overload within_frame(index)
# @param [Integer] index index of a frame (0 based)
def within_frame(*args, **kw_args)
switch_to_frame(_find_frame(*args, **kw_args))
beginView on GitHub (pinned to 15b5fdb76e)
Solutions
- Use within_frame for locating by name/id/index: within_frame('payment_iframe') { ... } or within_frame(0) { ... }
- If switching manually, find the element first: switch_to_frame(find(:frame, 'payment_iframe'))
- Remember only :parent/:top symbols and frame elements are valid for the manual API
Example fix
# before
switch_to_frame('payment_iframe') # ArgumentError
# after
within_frame('payment_iframe') { fill_in 'Card number', with: '4242' }
# or manual:
switch_to_frame(find(:frame, 'payment_iframe')) Defensive patterns
Strategy: type-guard
Validate before calling
switch_to_frame(find(:frame, 'payment_iframe')) if 'payment_iframe'.is_a?(String)
Type guard
def frame_target?(arg) arg.is_a?(Capybara::Node::Element) || %i[parent top].include?(arg) end switch_to_frame(arg) if frame_target?(arg)
Try / catch
begin
switch_to_frame(arg)
rescue ArgumentError
within_frame(arg) { yield } # fall back to the locating API
end Prevention
- Use within_frame for name/index/element addressing
- Reserve switch_to_frame for manual :parent/:top navigation with already-found elements
When it happens
Trigger: switch_to_frame('payment_iframe') (frame name as String); switch_to_frame(0) (index); switch_to_frame(nil); passing a locator string that would have worked with within_frame.
Common situations: Old Capybara 1.x habits where frame names were strings; reaching for switch_to_frame when within_frame(name_or_index) is the locating API; passing find results for non-frame elements.
Related errors
- `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
- `#within_window` requires a `Capybara::Window` instance or a
- Window cannot be switched inside a `within_frame` block
AI-assisted analysis of teamcapybara/capybara@15b5fdb76e (2026-08-21).
Data as JSON: /api/errors/50e74c7db2469f2f.
Report an issue: GitHub.