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
      end

View on GitHub (pinned to aa36b38e69)

Solutions

  1. Wrap the element or viewport in a ScrollOrigin: origin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element) then call scroll_from(origin, delta_x, delta_y).
  2. 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.
  3. If you only need to scroll an element into view, use scroll_to(element) which takes the element directly.
  4. 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

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


AI-assisted analysis of SeleniumHQ/selenium@aa36b38e69 (2026-08-14). Data as JSON: /api/errors/24926b7fe15212c5. Report an issue: GitHub.