Skip to content
Latchkey

Playwright "expect(page).toHaveURL(expected)" redirect race in CI

A toHaveURL check read the address before an async redirect (login, auth callback, client-side route) completed. The web-first assertion auto-retries, so a failure means the redirect never reached the expected URL in CI, or it was slower than the expect timeout.

What this error means

The diff shows the expected URL against the current pre-redirect one, and the test flakes: it depends on how fast the navigation settles on the CI runner.

playwright
Error: expect(page).toHaveURL(expected)
Expected pattern: /\/dashboard/
Received string:  "http://localhost:3000/login"
Call log:
  - expect.toHaveURL with timeout 5000ms

Common causes

The redirect had not completed when asserted

A client-side or auth redirect lands a moment after the action; a slow CI runner can read the URL while still on the source page.

The action that triggers navigation was not awaited

Clicking without awaiting the navigation lets the assertion run before the URL changes.

How to fix it

Wait for the navigation with the URL assertion

Use toHaveURL directly so Playwright polls until the address matches, rather than checking once.

tests/auth.spec.ts
await page.getByRole('button', { name: 'Log in' }).click();
await expect(page).toHaveURL(/\/dashboard/);

Increase the expect timeout for slow redirects

Raise the assertion timeout so a slower auth round trip in CI is not reported as a wrong URL.

tests/auth.spec.ts
await expect(page).toHaveURL(/\/dashboard/, { timeout: 15_000 });

How to prevent it

  • Await the action that triggers navigation.
  • Assert the destination URL with the retrying toHaveURL, not a one-shot read.
  • Allow extra timeout for auth redirects that hit external providers.

Frequently asked questions

What causes ""expect(page).toHaveURL" failed"?
A client-side or auth redirect lands a moment after the action; a slow CI runner can read the URL while still on the source page.
How do I fix "expect(page).toHaveURL" failed?
Use toHaveURL directly so Playwright polls until the address matches, rather than checking once.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card