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 500msNote 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.