{"record":{"id":"82d9397c0d215d7b","repo":"teamcapybara/capybara","slug":"block-passed-to-window-opened-by-opened-opened","errorCode":null,"errorMessage":"block passed to #window_opened_by opened #{opened_handles.size} windows instead of 1","messagePattern":"block passed to #window_opened_by opened #(.+?) windows instead of 1","errorType":"exception","errorClass":"Capybara::WindowError","httpStatus":null,"severity":"error","filePath":"lib/capybara/session.rb","lineNumber":588,"sourceCode":"    # It will wait for it to be opened (in the same way as other Capybara methods wait).\n    # It's better to use this method than `windows.last`\n    # {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}.\n    #\n    # @overload window_opened_by(**options, &block)\n    #   @param options [Hash]\n    #   @option options [Numeric] :wait  maximum wait time. Defaults to {Capybara.configure default_max_wait_time}\n    #   @return [Capybara::Window]       the window that has been opened within a block\n    #   @raise [Capybara::WindowError]   if block passed to window hasn't opened window\n    #     or opened more than one window\n    #\n    def window_opened_by(**options)\n      old_handles = driver.window_handles\n      yield\n\n      synchronize_windows(options) do\n        opened_handles = (driver.window_handles - old_handles)\n        if opened_handles.size != 1\n          raise Capybara::WindowError, 'block passed to #window_opened_by ' \\\n                                       \"opened #{opened_handles.size} windows instead of 1\"\n        end\n        Window.new(self, opened_handles.first)\n      end\n    end\n\n    ##\n    #\n    # Execute the given script, not returning a result. This is useful for scripts that return\n    # complex objects, such as jQuery statements. {#execute_script} should be used over\n    # {#evaluate_script} whenever possible.\n    #\n    # @param [String] script   A string of JavaScript to execute\n    # @param args  Optional arguments that will be passed to the script. Driver support for this is optional and types of objects supported may differ between drivers\n    #\n    def execute_script(script, *args)\n      @touched = true\n      driver.execute_script(script, *driver_args(args))","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/teamcapybara/capybara/blob/15b5fdb76e972e9623d5af2123ed3755594f9732/lib/capybara/session.rb#L570-L606","documentation":"window_opened_by snapshots driver.window_handles, runs the block, then - retrying until the wait time expires - requires exactly one new handle. Zero or multiple new windows raise Capybara::WindowError reporting the observed count. The retry loop means slow popups eventually resolve, but a popup that never appears or a burst of several windows is a hard failure.","triggerScenarios":"window_opened_by { click_link 'Share' } where the click does not open a window (popup blocked, JS error, wrong or overlapped element), or the action opens several tabs at once; OAuth/SSO popups that exceed default_max_wait_time.","commonSituations":"Headless Chrome with the click intercepted by an overlay or popups effectively blocked; popup opening after the default wait (slow third-party JS); app code legitimately opening two windows; clicks matching the wrong element so nothing happens.","solutions":["Verify the action really opens a window (run visibly in a real browser; assert the trigger element exists and the click fired)","Raise the wait: window_opened_by(wait: 5) { click_link 'Share' }","If several windows legitimately open, diff manually: snapshot page.windows before/after and select the one you need","For target=_blank links or window.open in the app, make sure driver options are not blocking popups"],"exampleFix":"# before\nwindow_opened_by { click_link 'Tweet' } # WindowError: opened 0 windows\n\n# after\nwindow = window_opened_by(wait: 5) { click_link 'Tweet' }\nwithin_window(window) { click_link 'Authorize' }","handlingStrategy":"retry","validationCode":"before_handles = page.driver.window_handles\nclick_link 'Share'\nnew_handles = page.driver.window_handles - before_handles\nswitch_to_window(page.windows.find { |w| new_handles.include?(w.handle) }) if new_handles.size == 1","typeGuard":null,"tryCatchPattern":"begin\n  window_opened_by(wait: Capybara.default_max_wait_time) { click_link 'Share' }\nrescue Capybara::WindowError => e\n  raise \"#{e.message} - open windows: #{page.windows.map(&:title).inspect}\"\nend","preventionTips":["Pass wait: explicitly for OAuth/popup flows","Assert the trigger element exists and is clickable before the block","For multi-window flows, use manual handle diffing instead of window_opened_by"],"tags":["windows","popup","window-opened-by","wait","timing"],"backgroundTag":"window-not-opened","analyzedSha":"15b5fdb76e972e9623d5af2123ed3755594f9732","analyzedAt":"2026-08-21T16:53:45.588Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}