Why Browser Agents Click the Wrong Element

7

min read

Tutorials

When a browser agent misclicks, it's rarely because the model reasoned badly. It's because the page handed it a signal that looked right and wasn't. An agent clicks the wrong element when the thing it's using to identify "the button" — visible text, screen coordinates, or a raw DOM node — doesn't actually correspond to what's clickable, enabled, or intended on that specific page. The mismatch happens below the reasoning layer, which is why it's so hard to debug by reading the agent's logic.

Below are the concrete ways this happens, in the order we see them most often in production traffic.

Two elements with the same name and role

The most common failure isn't exotic. A page has two buttons labeled "Add to Cart" — one for the product, one hidden in a "customers also bought" carousel. Both have role="button", both have the same accessible name. Anything identifying elements by name+role alone can't tell them apart, and picks whichever one it encounters first in DOM order, which has no relationship to which one the user (or agent) meant.

This shows up constantly on pages with repeated card layouts: product grids, comparison tables, pricing tiers. Every "Select" or "Choose plan" button collides with its siblings unless something also captures position, containing section, or nearby disambiguating text.

Elements that are visually gone but still "there"

A button can have real DOM presence, a real accessible name, and still not be something a user could click. aria-hidden="true" on a parent two levels up removes an element from the accessibility tree even though the node itself has no aria-hidden attribute — an agent that only checks the element in isolation misses this and treats it as valid.

The inverse also happens: opacity: 0 elements (common with scroll-triggered reveal animations like Framer Motion or GSAP) are still named and still technically clickable in the DOM, but invisible to a real user until they scroll further. An agent that clicks it anyway isn't wrong about the element's existence — it's wrong about timing.

Disabled elements that don't look disabled

Plenty of "Add to Cart" or "Continue" buttons aren't disabled in the HTML sense. They validate on click via a JS handler, checking whether a required radio group or size selector has a value yet. There's no aria-disabled, no grayed-out styling necessarily — just a click that silently no-ops or throws a validation toast the agent didn't anticipate. From the DOM's perspective the button is a normal, enabled, clickable element. It only becomes clear that it was gated after the click fails to produce the expected page change.

Actions that depend on other actions

Related to the above: a lot of "wrong click" incidents aren't clicking element A when B was correct — they're clicking B before A. A submit button requires a form field to be filled first. A checkout button requires a shipping method to be selected. Nothing about the button itself signals this; the dependency lives in application logic, not markup. An agent without a model of which actions unlock which other actions will hit these in the wrong order every time the flow isn't purely linear.

Content the DOM doesn't expose directly

Shadow DOM and slotted content are increasingly common in component libraries (custom <select> replacements, design-system buttons built as web components). The accessible name for these elements often lives inside a shadow root or an assigned slot, not in the light DOM a naive scraper walks. Miss that traversal step and the element either has no name at all, or picks up the wrong text from a sibling that happens to be easier to find.

The page isn't the page you asked for

Geo and locale redirects change what's on the page entirely, independent of anything an agent does. A real browser hitting a product page from certain IP ranges gets redirected to a locale-specific version with a different layout, different button labels, sometimes a cookie-consent gate the original URL didn't have. An agent that cached its understanding of "this page" from a prior run, or from documentation, is now acting on a page that no longer exists in that form.

Stale state from caching or reused sessions

Any system that caches a page's structure to save on repeated extraction cost has to invalidate that cache when the page actually changes — a redesign, an A/B test, a seasonal banner. Serving a six-hour-old structural snapshot of a page that shipped a UI change four hours ago produces confident, wrong element references. This is a cost/reliability tradeoff every extraction pipeline has to make explicitly, not something that resolves itself.

Why this keeps happening

Two dominant approaches to giving agents access to the web each have a blind spot that produces exactly this failure mode.

Vision-based agents take a screenshot and click coordinates. They're blind to all of the above except the purely visual cases (opacity, layout) — and even then, coordinate-based clicking breaks the instant a page reflows, loads slower than expected, or renders at a different viewport size than the screenshot assumed.

Raw DOM or accessibility-tree scraping solves some of this — aria_snapshot()-style output is a real improvement over guessing from pixels — but it wasn't built to answer "is this clickable right now" or "what does this depend on." It tells you an element exists and what it's named. It doesn't tell you it's one of four duplicates, that it's gated behind an unrelated field, or that the page redirected you somewhere else entirely. Closing that gap requires cross-referencing the accessibility tree against the actual DOM state — disabled attributes, required-field relationships, visibility computed the way a renderer computes it, not just presence in markup.

That's a narrower, more mechanical problem than "understand the page," and it's the one worth solving directly rather than working around with better prompting.

FAQ

Why do AI agents click the wrong button on a webpage?
Because the signal they use to identify a button — visible text, coordinates, or a bare DOM reference — doesn't reliably map to which element is actually clickable, enabled, and intended at that moment. Duplicate elements, hidden text, disabled-but-unmarked buttons, and stale page state are the most common specific causes.

Is this a model reasoning problem or a data problem?
Almost always a data problem. The agent reasoned correctly given what it was told about the page; what it was told was incomplete or wrong. Better prompting doesn't fix a page description that's missing the fact that two buttons share a name, or that a button is gated behind a field the model was never told about.

Does using an accessibility tree instead of raw HTML fix this?
It fixes some of it — accessible names and roles are a real improvement over parsing raw markup — but not all of it. Accessibility trees don't encode disabled-via-JS-handler state, cross-element dependencies, or which of several identically-named elements is the intended one. Those require cross-referencing the tree against the live DOM.

How can I tell if a browser agent failure was a wrong-element click versus something else?
Check whether the agent's action target had a real accessible name and appeared clickable at click time. If it did, and the click still produced the wrong outcome, look for duplicates with the same name/role, a JS-gated disabled state, or an unmet dependency (a required field the button needed filled first) — in roughly that order of likelihood.

Does this problem get worse on redesigned or A/B-tested pages?
Yes. Anything that caches a structural understanding of a page — whether that's a fine-tuned selector, a hardcoded XPath, or a cached extraction — goes stale the moment the page changes. Sites running active A/B tests or frequent redesigns are the highest-risk targets for this specific failure mode.

omfang logo

Follow us on social media

Contact us

Learn more about omfang