apify/crawlee · error · TimeoutError

Navigation timed out after ${this.#navigationTimeoutMillis /

Error message

Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.

What it means

Browser crawlers wrap navigation steps in a windowGuard middleware that enforces the crawler's navigationTimeoutMillis budget. If the remaining navigation window is exhausted (or already <= 0) before a navigation step runs, a TimeoutError is thrown stating the total navigation time budget that elapsed.

Source

Thrown at packages/browser-crawler/src/internals/browser-crawler.ts:476

            action: (ctx: Ctx) => Awaitable<void | Partial<Ctx>>,
        ): ContextMiddleware<Ctx, Partial<Ctx>> => ({
            action: async (ctx) => (ctx.request.skipNavigation ? {} : ((await action(ctx)) ?? {})),
        });

        super({
            ...basicCrawlerOptions,
            contextPipelineBuilder: () => {
                // A single navigation window covers the pre-navigation hooks, the navigation, and the
                // post-navigation hooks: the whole phase shares one `navigationTimeoutSecs` budget, so a slow
                // hook eats into the same window the navigation uses. The navigation itself is bounded by
                // capping its `gotoOptions.timeout` to the remaining budget.
                const windowGuard = <Ctx extends Context>(
                    step: (ctx: Ctx) => Awaitable<void | Partial<Ctx>>,
                ): ContextMiddleware<Ctx, Partial<Ctx>> =>
                    skipGuard(async (ctx: Ctx) => {
                        const remaining = remainingNavigationWindowMillis(ctx, this.#navigationTimeoutMillis);
                        if (remaining <= 0) {
                            throw new TimeoutError(
                                `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`,
                            );
                        }
                        return addTimeoutToPromise(
                            async () => step(ctx),
                            remaining,
                            `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`,
                        );
                    });

                let pipeline = contextPipelineBuilder().compose({ action: this.prepareNavigation.bind(this) });

                for (const hook of this.#preNavigationHooks) {
                    pipeline = pipeline.compose(windowGuard(hook));
                }

                pipeline = pipeline.compose(skipGuard(this.navigate.bind(this)));

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Increase navigationTimeoutMillis in the crawler options
  2. Speed up page loads (block unnecessary resources, disable images) so navigation fits in the budget
  3. Handle the TimeoutError in the request handler / rely on crawler retry behavior for transient slowness

Example fix

// before
const crawler = new PuppeteerCrawler({ navigationTimeoutMillis: 15000 });
// after
const crawler = new PuppeteerCrawler({ navigationTimeoutMillis: 60000 });
Defensive patterns

Strategy: retry

Try / catch

try { /* navigation happens inside crawler.run */ } catch (e) { if (e instanceof TimeoutError && /Navigation timed out/.test(e.message)) { /* retry request or raise navigationTimeoutMillis */ } else throw e; }

Prevention

When it happens

Trigger: The cumulative time spent on navigation (goto plus related steps) exceeds navigationTimeoutMillis; a previous navigation consumed most of the budget and a subsequent step starts with no time left; navigationTimeoutMillis set too low for a slow site.

Common situations: Slow sites or heavy pages exceeding the default 60s navigation timeout; flaky networks; users lowering navigationTimeoutMillis aggressively; multiple enqueuing/navigation steps sharing one budget.

Understand the failure class

Related errors


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/7f4d3fdb4bb8e6e4. Report an issue: GitHub.