{"record":{"id":"8bfe295f0fd54cb8","repo":"apify/crawlee","slug":"page-object-was-used-in-http-only-request-handler","errorCode":null,"errorMessage":"Page object was used in HTTP-only request handler","messagePattern":"Page object was used in HTTP-only request handler","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/playwright-crawler/src/internals/adaptive-playwright-crawler.ts","lineNumber":512,"sourceCode":"                    throw new Error(errorMessage('querySelectorAll'));\n                },\n                get waitForSelector(): AdaptivePlaywrightCrawlerContext['waitForSelector'] {\n                    throw new Error(errorMessage('waitForSelector'));\n                },\n                get parseWithCheerio(): AdaptivePlaywrightCrawlerContext['parseWithCheerio'] {\n                    throw new Error(errorMessage('parseWithCheerio'));\n                },\n                get enqueueLinks(): AdaptivePlaywrightCrawlerContext['enqueueLinks'] {\n                    throw new Error(errorMessage('enqueueLinks'));\n                },\n            }),\n        });\n    }\n\n    private async adaptCheerioContext(cheerioContext: CheerioCrawlingContext) {\n        return {\n            get page(): Page {\n                throw new Error('Page object was used in HTTP-only request handler');\n            },\n            async querySelector(selector: string) {\n                return cheerioContext.$(selector).first();\n            },\n            async querySelectorAll(selector: string) {\n                return cheerioContext.$(selector);\n            },\n            enqueueLinks: async (options: EnqueueLinksOptions = {}) => {\n                const urls = extractUrlsFromCheerio(\n                    cheerioContext.$,\n                    options.selector,\n                    options.baseUrl ?? cheerioContext.request.loadedUrl,\n                );\n                return (await this.enqueueLinks(urls, options, cheerioContext.request)) as unknown as void;\n            },\n            response: cheerioContext.response,\n        };\n    }","sourceCodeStart":494,"sourceCodeEnd":530,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/playwright-crawler/src/internals/adaptive-playwright-crawler.ts#L494-L530","documentation":"When AdaptivePlaywrightCrawler renders a page via the HTTP-only (cheerio) path, there is no browser Page object. The adapted context exposes a `page` getter that throws this error to prevent developers from accidentally using a nonexistent Playwright Page in an HTTP-only request handler.","triggerScenarios":"Accessing context.page inside a requestHandler when the crawler processed the request over plain HTTP (no browser was launched for that request).","commonSituations":"Handler written for PlaywrightCrawler reused with AdaptivePlaywrightCrawler; page.screenshot() or page.evaluate() called without checking which rendering path handled the request; intermittent failures because only some requests get browser rendering.","solutions":["Remove or guard usage of context.page so the handler works without a browser Page (use cheerio-based querySelector/querySelectorAll).","Force the request through the browser pipeline (e.g., via rendering type hints in request.userData or crawler configuration) whenever page access is genuinely required.","Split logic: run page-dependent work in a PlaywrightCrawler and HTTP work in a CheerioCrawler/adaptive handler that never touches page."],"exampleFix":"// before\nasync requestHandler(context) {\n    await context.page.screenshot({ path: 'shot.png' });\n}\n// after\nasync requestHandler(context) {\n    if (context.page) {\n        await context.page.screenshot({ path: 'shot.png' });\n    }\n}","handlingStrategy":"type-guard","validationCode":"// before touching page\nif (!context.page || isClosedPage(context.page)) skipPageWork();","typeGuard":"function hasLivePage(ctx: { page?: unknown }): ctx is { page: Page } {\n    const p = ctx.page as Page | undefined;\n    return !!p && typeof p.screenshot === 'function' && !p.isClosed();\n}","tryCatchPattern":"try {\n    await context.page.screenshot({ path: 'shot.png' });\n} catch (err) {\n    if ((err as Error).message === 'Page object was used in HTTP-only request handler') {\n        log.info('Skipping screenshot: HTTP-only rendering');\n    } else throw err;\n}","preventionTips":["Guard all context.page usages in adaptive handlers.","Move screenshot/evaluate logic into PlaywrightCrawler if it always requires a Page.","Remember rendering mode is per-request and can differ between runs."],"tags":["adaptive-crawler","playwright","http-only","crawlee"],"backgroundTag":"api-not-available-in-context","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}