{"record":{"id":"7f4d3fdb4bb8e6e4","repo":"apify/crawlee","slug":"navigation-timed-out-after-this-navigationtimeo","errorCode":null,"errorMessage":"Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.","messagePattern":"Navigation timed out after (.+?) seconds\\.","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"packages/browser-crawler/src/internals/browser-crawler.ts","lineNumber":476,"sourceCode":"            action: (ctx: Ctx) => Awaitable<void | Partial<Ctx>>,\n        ): ContextMiddleware<Ctx, Partial<Ctx>> => ({\n            action: async (ctx) => (ctx.request.skipNavigation ? {} : ((await action(ctx)) ?? {})),\n        });\n\n        super({\n            ...basicCrawlerOptions,\n            contextPipelineBuilder: () => {\n                // A single navigation window covers the pre-navigation hooks, the navigation, and the\n                // post-navigation hooks: the whole phase shares one `navigationTimeoutSecs` budget, so a slow\n                // hook eats into the same window the navigation uses. The navigation itself is bounded by\n                // capping its `gotoOptions.timeout` to the remaining budget.\n                const windowGuard = <Ctx extends Context>(\n                    step: (ctx: Ctx) => Awaitable<void | Partial<Ctx>>,\n                ): ContextMiddleware<Ctx, Partial<Ctx>> =>\n                    skipGuard(async (ctx: Ctx) => {\n                        const remaining = remainingNavigationWindowMillis(ctx, this.#navigationTimeoutMillis);\n                        if (remaining <= 0) {\n                            throw new TimeoutError(\n                                `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`,\n                            );\n                        }\n                        return addTimeoutToPromise(\n                            async () => step(ctx),\n                            remaining,\n                            `Navigation timed out after ${this.#navigationTimeoutMillis / 1000} seconds.`,\n                        );\n                    });\n\n                let pipeline = contextPipelineBuilder().compose({ action: this.prepareNavigation.bind(this) });\n\n                for (const hook of this.#preNavigationHooks) {\n                    pipeline = pipeline.compose(windowGuard(hook));\n                }\n\n                pipeline = pipeline.compose(skipGuard(this.navigate.bind(this)));\n","sourceCodeStart":458,"sourceCodeEnd":494,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/browser-crawler/src/internals/browser-crawler.ts#L458-L494","documentation":"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.","triggerScenarios":"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.","commonSituations":"Slow sites or heavy pages exceeding the default 60s navigation timeout; flaky networks; users lowering navigationTimeoutMillis aggressively; multiple enqueuing/navigation steps sharing one budget.","solutions":["Increase navigationTimeoutMillis in the crawler options","Speed up page loads (block unnecessary resources, disable images) so navigation fits in the budget","Handle the TimeoutError in the request handler / rely on crawler retry behavior for transient slowness"],"exampleFix":"// before\nconst crawler = new PuppeteerCrawler({ navigationTimeoutMillis: 15000 });\n// after\nconst crawler = new PuppeteerCrawler({ navigationTimeoutMillis: 60000 });","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Size navigationTimeoutMillis above your slowest target page's load time","Block heavy resources to keep navigation fast","Rely on crawler maxRequestRetries for transient slowness"],"tags":["timeout","navigation","browser"],"backgroundTag":"navigation-timeout","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}