Skip to content

click reports success on a disabled button that has a click listener #5964

Description

@yotambraun

Browser Use Version

0.13.10 (main at 4cbe921)

Bug Description, Steps to Reproduce, Screenshots

A disabled form control with a click listener is offered to the agent (the serialized DOM shows it as [N]<button disabled=true />), and click on it returns a success result:

error:             None
extracted_content: Clicked button "Place order" id=place
handler ran:       False

The browser never dispatches clicks to a disabled control, so nothing happened. Because error is not set, multi_act() keeps running the queued actions (it stops on error), a single-action step is not counted toward max_failures, and the model is told the click worked.

Why the control is offered at all: is_interactive() returns early for elements with a JS click listener, before its accessibility disabled check (dom/serializer/clickable_elements.py). Without a listener the same disabled button is not offered. Framework buttons are affected the same way: I checked a disabled Vue 3 button with @click and a disabled React 18 button with onClick; both are offered and click returns Clicked button "Place order".

Where it shows up: a button that the page enables asynchronously after input, for example after a debounced availability check. When the model types and clicks in the same step, the click lands while the button is still disabled and comes back as Clicked ....

Probes against local pages, real Vue/React pages and the raw data are here: https://github.com/yotambraun/browser-use/tree/69c69ad6abdc474a2475d6cb10449571b5d49fac

Failing Python Code

import asyncio

from pytest_httpserver import HTTPServer

from browser_use.browser import BrowserProfile, BrowserSession
from browser_use.tools.service import Tools

PAGE = """<html><body>
<button id="place" disabled>Place order</button>
<script>
  document.getElementById('place').addEventListener('click', () => { window.ordered = true })
</script>
</body></html>"""


async def main():
	server = HTTPServer()
	server.start()
	server.expect_request('/').respond_with_data(PAGE, content_type='text/html')
	session = BrowserSession(browser_profile=BrowserProfile(headless=True, user_data_dir=None))
	await session.start()
	tools = Tools()
	await tools.navigate(url=server.url_for('/'), new_tab=False, browser_session=session)
	await session.get_browser_state_summary()
	index = next(i for i, el in (await session.get_selector_map()).items() if el.attributes.get('id') == 'place')

	result = await tools.click(index=index, browser_session=session)
	cdp = await session.get_or_create_cdp_session()
	ordered = await cdp.cdp_client.send.Runtime.evaluate(
		params={'expression': 'window.ordered === true', 'returnByValue': True}, session_id=cdp.session_id
	)
	print('error:            ', result.error)
	print('extracted_content:', result.extracted_content)
	print('handler ran:      ', ordered['result']['value'])
	await session.kill()
	server.stop()


asyncio.run(main())

LLM Model

Not needed for the reproduction (the action is called directly).

Operating System & Browser Versions

Linux (WSL2), Chromium from browser-use install, headless.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions