Jump to content

Connect Leeroopedia MCP: Equip your AI agents to search best practices, build plans, verify code, diagnose failures, and look up hyperparameter defaults.

Heuristic:Puppeteer Puppeteer Navigation Race Condition Avoidance

From Leeroopedia
Knowledge Sources
Domains Browser_Automation, Debugging
Last Updated 2026-02-11 23:30 GMT

Overview

Always start `waitForNavigation()` before the action that triggers navigation (e.g., `click()`) using `Promise.all()` to avoid race conditions.

Description

A common Puppeteer pitfall is calling `click()` on a link or button that triggers a page navigation, then calling `waitForNavigation()` afterward. If the navigation completes before `waitForNavigation()` is called, the promise will never resolve (or will time out) because the navigation event was already fired and missed. The correct pattern is to start waiting for navigation before triggering the action.

Usage

Apply this heuristic any time a user action triggers a page navigation: clicking links, submitting forms, calling `evaluate()` with `location.href` changes, or any other action that causes the browser to navigate. This is one of the most common sources of timeout errors in Puppeteer scripts.

The Insight (Rule of Thumb)

  • Action: Use `Promise.all([page.waitForNavigation(), page.click(selector)])` to start the navigation listener before the click fires.
  • Value: Eliminates the most common source of non-deterministic timeout failures in Puppeteer scripts.
  • Trade-off: Slightly more verbose code. Alternatively, use the Locator API which handles this automatically.

Reasoning

The browser processes navigation events asynchronously. When `click()` triggers a navigation:

  1. The click fires a navigation event in the browser
  2. The navigation event is emitted to listeners
  3. If no listener is registered yet, the event is lost

By calling `waitForNavigation()` first (inside `Promise.all`), the listener is registered before the click fires, guaranteeing the navigation event is captured.

Incorrect pattern (from Puppeteer API docs):

// WRONG — Race condition: navigation may complete before waitForNavigation
const navPromise = page.waitForNavigation();
await page.click('a.my-link');
await navPromise;  // May timeout if navigation already finished

Correct pattern (from `packages/puppeteer-core/src/api/Page.ts:2803-2815`):

// CORRECT — Start waiting before triggering navigation
const [response] = await Promise.all([
  page.waitForNavigation(waitOptions),
  page.click(selector, clickOptions),
]);

This pattern is documented in the JSDoc for `Frame.waitForNavigation()` at `packages/puppeteer-core/src/api/Frame.ts:1040-1053`.

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment