Playwright "Test timeout of 30000ms exceeded" in CI
By Daniel Zoghalchali·Latchkey
A Playwright test exceeded its time budget (30 s by default). An action waited on a locator or navigation that never reached the expected state - often just slower in CI, sometimes a genuinely wrong selector.
What this error means
A test fails with "Test timeout of 30000ms exceeded" plus a "waiting for locator(...)" call log showing what it was stuck on. It commonly passes locally and times out on a slower CI runner.
Playwright output
Test timeout of 30000ms exceeded.
Error: locator.click: Timeout 30000ms exceeded.
Call log:
- waiting for locator('button[name="submit"]')
Common causes
App slower in CI than locally
Navigation or rendering takes longer on a loaded runner, so the auto-waiting action exceeds the timeout even though the app is correct.
Locator never resolves
The selector matches nothing (markup changed, element in another frame, or behind an unmet condition), so the wait runs to the full timeout.
How to fix it
Use web-first assertions and set sane timeouts
Let Playwright auto-wait on expectations, and raise the per-test/expect timeout for slow CI in config.
Use await expect(locator).toBeVisible() instead of bare clicks that hang.
Run with --trace on and open the trace to see exactly where it stalled.
If the locator matches nothing, fix the selector - a longer timeout will not help.
How to prevent it
Tune timeout/expect.timeout per CI tier in config.
Use role/test-id locators and web-first expect assertions.
Capture traces on first retry to debug timeouts fast.
Frequently asked questions
What causes ""Timeout 30000ms exceeded""?
Navigation or rendering takes longer on a loaded runner, so the auto-waiting action exceeds the timeout even though the app is correct.
How do I fix "Timeout 30000ms exceeded"?
Let Playwright auto-wait on expectations, and raise the per-test/expect timeout for slow CI in config.
Can Latchkey fix this automatically?
Yes. Latchkey runs your GitHub Actions on managed runners that detect this failure, apply the fix, and retry the job automatically - self-healing is on by default.