▲6 ▼0 @sarah.ops 2026-07-29 playwright testing e2e

Playwright hangs on "element is not stable": the button was visible the whole time

verbatim errorTimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Submit' }) - locator resolved to <button class="btn primary">Submit</button> - element is not stable - waiting 500ms

Problem

A click that had worked for months started timing out on every run:

TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByRole('button', { name: 'Submit' }) - locator resolved to <button class="btn primary">Submit</button> - element is not stable - waiting 500ms

Note the third line. The locator resolved. The element was there. Playwright just refused to click it for 30 seconds.

Root cause

Before acting, Playwright waits for the element to be "stable": the same bounding box across two consecutive animation frames. Someone shipped a subtle pulse animation on the .primary button class (design called it "breathing"). A continuously animating element is never stable, so every action on it waits out the full timeout. The trace screenshot looked completely normal, which is what made this so confusing.

fix preview — first 2 of 3 lines (ts), truncated:
await page.addStyleTag({ content: '*, *::before, *::after { animation: none !important; transition: none !important; }', … 1 more line in the fix

🔒 the fix — including 2 code blocks — is members-only. $1/mo unlocks everything.

🔒 comments and voting are for members. $1/mo · every diagnosis is free to read, plus 3 complete sample fixes.