{"record":{"id":"24926b7fe15212c5","repo":"SeleniumHQ/selenium","slug":"scroll-origin-inspect-isn-t-a-valid-scrollorigi","errorCode":null,"errorMessage":"#{scroll_origin.inspect} isn't a valid ScrollOrigin","messagePattern":"#(.+?) isn't a valid ScrollOrigin","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"rb/lib/selenium/webdriver/common/interactions/wheel_actions.rb","lineNumber":89,"sourceCode":"      #    origin = WheelActions::ScrollOrigin.element(el)\n      #    driver.action.scroll_from(origin, 0, 200).perform\n      #\n      # @example Scroll from element by a specified amount with an offset\n      #    el = driver.find_element(id: \"some_id\")\n      #    origin = WheelActions::ScrollOrigin.element(el, 10, 10)\n      #    driver.action.scroll_from(origin, 100, 200).perform\n      #\n      # @example Scroll viewport by a specified amount with an offset\n      #    origin = WheelActions::ScrollOrigin.viewport(10, 10)\n      #    driver.action.scroll_from(origin, 0, 200).perform\n      #\n      # @param [ScrollOrigin] scroll_origin Where scroll originates (viewport or element center) plus provided offsets.\n      # @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.\n      # @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.\n      # @return [Selenium::WebDriver::WheelActions] A self reference.\n      # @raise [Error::MoveTargetOutOfBoundsError] If the origin with offset is outside the viewport.\n      def scroll_from(scroll_origin, delta_x, delta_y, device: nil)\n        raise TypeError, \"#{scroll_origin.inspect} isn't a valid ScrollOrigin\" unless scroll_origin.is_a?(ScrollOrigin)\n\n        scroll(x: scroll_origin.x_offset,\n               y: scroll_origin.y_offset,\n               delta_x: delta_x,\n               delta_y: delta_y,\n               origin: scroll_origin.origin,\n               device: device)\n      end\n\n      private\n\n      def scroll(**opts)\n        opts[:duration] = default_scroll_duration\n        wheel = wheel_input(opts.delete(:device))\n        wheel.create_scroll(**opts)\n        tick(wheel)\n        self\n      end","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/SeleniumHQ/selenium/blob/aa36b38e696a0909e973bdf5e2f9031ffe842c4b/rb/lib/selenium/webdriver/common/interactions/wheel_actions.rb#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\ndriver.action.scroll_from(element, 0, 200).perform\n\n# after\norigin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element)\ndriver.action.scroll_from(origin, 0, 200).perform","handlingStrategy":"type-guard","validationCode":"origin_is_valid = scroll_origin.is_a?(Selenium::WebDriver::WheelActions::ScrollOrigin)","typeGuard":"def scroll_origin?(obj)\n  obj.is_a?(Selenium::WebDriver::WheelActions::ScrollOrigin)\nend","tryCatchPattern":"begin\n  driver.action.scroll_from(origin, dx, dy).perform\nrescue TypeError => e\n  raise unless e.message.include?('ScrollOrigin')\n  # rebuild origin from a raw element and retry\n  origin = Selenium::WebDriver::WheelActions::ScrollOrigin.element(element)\n  driver.action.scroll_from(origin, dx, dy).perform\nend","preventionTips":["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."],"tags":["ruby","scroll","interactions","type-error","wheel-actions"],"backgroundTag":null,"analyzedSha":"aa36b38e696a0909e973bdf5e2f9031ffe842c4b","analyzedAt":"2026-08-14T02:32:32.244Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}