SeleniumHQ/selenium · error · TypeError
#{scroll_origin.inspect} isn't a valid ScrollOrigin
Error message
#{scroll_origin.inspect} isn't a valid ScrollOrigin What it means
The scroll_from method performs a mouse-wheel scroll relative to an origin described by a WheelActions::ScrollOrigin object. It raises TypeError when the first argument is not a ScrollOrigin instance, because the action builder needs the structured origin (a WebElement or the viewport plus x/y offsets) to construct the W3C scroll action. ScrollOrigin objects must be created through the ScrollOrigin.element or ScrollOrigin.viewport factory methods.
Source
Thrown at rb/lib/selenium/webdriver/common/interactions/wheel_actions.rb:89
# origin = WheelActions::ScrollOrigin.element(el)
# driver.action.scroll_from(origin, 0, 200).perform
#
# @example Scroll from element by a specified amount with an offset
# el = driver.find_element(id: "some_id")
# origin = WheelActions::ScrollOrigin.element(el, 10, 10)
# driver.action.scroll_from(origin, 100, 200).perform
#
# @example Scroll viewport by a specified amount with an offset
# origin = WheelActions::ScrollOrigin.viewport(10, 10)
# driver.action.scroll_from(origin, 0, 200).perform
#
# @param [ScrollOrigin] scroll_origin Where scroll originates (viewport or element center) plus provided offsets.
# @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
# @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
# @return [Selenium::WebDriver::WheelActions] A self reference.
# @raise [Error::MoveTargetOutOfBoundsError] If the origin with offset is outside the viewport.
def scroll_from(scroll_origin, delta_x, delta_y, device: nil)
raise TypeError, "#{scroll_origin.inspect} isn't a valid ScrollOrigin" unless scroll_origin.is_a?(ScrollOrigin)
scroll(x: scroll_origin.x_offset,
y: scroll_origin.y_offset,
delta_x: delta_x,
delta_y: delta_y,
origin: scroll_origin.origin,
device: device)
end
private
def scroll(**opts)
opts[:duration] = default_scroll_duration
wheel = wheel_input(opts.delete(:device))
wheel.create_scroll(**opts)
tick(wheel)
self
endView on GitHub (pinned to aa36b38e69)
Solutions
- Wrap the element or viewport in a ScrollOrigin: origin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element) then call scroll_from(origin, delta_x, delta_y).
- If you just need to scroll the viewport by an amount, use the simpler scroll_by(delta_x, delta_y) helper which does not need a ScrollOrigin.
- If you only need to scroll an element into view, use scroll_to(element) which takes the element directly.
- For viewport-relative scrolling with an offset, use ScrollOrigin.viewport(x_offset, y_offset).
Example fix
# before driver.action.scroll_from(element, 0, 200).perform # after origin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element) driver.action.scroll_from(origin, 0, 200).perform
Defensive patterns
Strategy: type-guard
Validate before calling
origin_is_valid = scroll_origin.is_a?(Selenium::WebDriver::WheelActions::ScrollOrigin)
Type guard
def scroll_origin?(obj) obj.is_a?(Selenium::WebDriver::WheelActions::ScrollOrigin) end
Try / catch
begin
driver.action.scroll_from(origin, dx, dy).perform
rescue TypeError => e
raise unless e.message.include?('ScrollOrigin')
# rebuild origin from a raw element and retry
origin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element)
driver.action.scroll_from(origin, dx, dy).perform
end Prevention
- Always construct the origin via ScrollOrigin.element or ScrollOrigin.viewport before calling scroll_from.
- Prefer the scroll_to(element) and scroll_by(dx, dy) helpers when you do not need explicit offsets.
- Add an is_a?(ScrollOrigin) guard in helper methods that wrap scroll_from.
When it happens
Trigger: Calling driver.action.scroll_from(element, 0, 200) and passing a raw WebElement instead of ScrollOrigin.element(element). Passing a Hash, Array, String, or nil as the scroll_origin argument. Mixing up scroll_from (which requires a ScrollOrigin) with the scroll_to(element) convenience wrapper that accepts an element directly.
Common situations: A developer copies the scroll_to example but switches to scroll_from without wrapping the element in ScrollOrigin. Passing a tuple/Hash such as {element: el, x: 10} thinking scroll_from parses it. Using scroll_from where scroll_by(delta_x, delta_y) would have been simpler.
Related errors
- #{action.inspect} is not a valid action
- #{source.type} is not a valid input type
- #{source.type} is not a valid input type
- Unknown options found: #{@opts.inspect}
- #{num} is not a #{klass}
AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14).
Data as JSON: /api/errors/24926b7fe15212c5.
Report an issue: GitHub.