{"record":{"id":"987205be35e7ccd9","repo":"apify/crawlee","slug":"cannot-extract-links-because-the-dom-is-not-availa-987205","errorCode":null,"errorMessage":"Cannot extract links because the DOM is not available.","messagePattern":"Cannot extract links because the DOM is not available\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/linkedom-crawler/src/internals/linkedom-crawler.ts","lineNumber":277,"sourceCode":"                    get document(): Document {\n                        throw new NavigationSkippedError(\n                            'The `document` property is not available - `skipNavigation` was used',\n                            { cause: err },\n                        );\n                    },\n                };\n            }\n\n            throw err;\n        }\n    }\n\n    private async addHelpers(crawlingContext: InternalHttpCrawlingContext & { body: string; window: Window }) {\n        const addRequests = crawlingContext.addRequests;\n\n        const extractLinks = async (options?: ExtractLinksOptions): Promise<string[]> => {\n            if (!crawlingContext.window) {\n                throw new Error('Cannot extract links because the DOM is not available.');\n            }\n\n            return extractUrlsFromWindow(\n                crawlingContext.window,\n                options?.selector ?? 'a',\n                options?.baseUrl ?? crawlingContext.request.loadedUrl ?? crawlingContext.request.url,\n            );\n        };\n\n        return {\n            extractLinks,\n            enqueueLinks: async (options: EnqueueLinksOptions = {}) => {\n                const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({\n                    enqueueStrategy: options.strategy,\n                    finalRequestUrl: crawlingContext.request.loadedUrl,\n                    originalRequestUrl: crawlingContext.request.url,\n                    userProvidedBaseUrl: options.baseUrl,\n                });","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/linkedom-crawler/src/internals/linkedom-crawler.ts#L259-L295","documentation":"LinkedomCrawler's extractLinks helper needs a parsed DOM (the `window` object created from the response body) to select anchor elements and extract URLs. When the crawler did not parse the page into a linkedom window — e.g. the response was empty, non-HTML, or parsing was skipped — `crawlingContext.window` is undefined and this error is thrown instead of silently returning no links.","triggerScenarios":"Calling `context.extractLinks()` (directly or via `enqueueLinks` behavior routed through `urls`) inside a request handler of LinkedomCrawler when `crawlingContext.window` is undefined, typically because the response body could not be parsed into a DOM.","commonSituations":"Crawling endpoints that return empty bodies, non-HTML content types (JSON, plain text, binary), or responses that failed to load; handlers that call extractLinks on error pages; misconfigured content-type handling that skips DOM parsing.","solutions":["Check that the response actually returned HTML before calling extractLinks (inspect `context.response.headers['content-type']` and `context.body`).","Log `context.body` in the handler to confirm a non-empty HTML document was received; skip link extraction when it is empty or non-HTML.","Ensure the request succeeded (status 200) and the site is not blocking the crawler with an empty or stub response.","If pages legitimately mix HTML and non-HTML responses, branch the handler: parse links only for HTML responses."],"exampleFix":"// before\nawait context.extractLinks();\n\n// after\nconst contentType = context.response?.headers?.['content-type'] ?? '';\nif (contentType.includes('text/html')) {\n    await context.extractLinks();\n}","handlingStrategy":"validation","validationCode":"const contentType = context.response?.headers?.['content-type'] ?? '';\nif (!contentType.includes('text/html') || !context.body) {\n    log.warning('Skipping link extraction: no HTML DOM available.');\n    return;\n}","typeGuard":"function hasDom(ctx): ctx is typeof ctx & { window: NonNullable<typeof ctx.window> } {\n    return Boolean((ctx as { window?: unknown }).window);\n}","tryCatchPattern":"try {\n    const links = await context.extractLinks();\n} catch (err) {\n    if (err.message.includes('DOM is not available')) {\n        log.warning('No DOM parsed; skipping link extraction.');\n        return;\n    }\n    throw err;\n}","preventionTips":["Only call extractLinks on responses with an HTML content type and non-empty body.","Log the response status/content-type in the handler during development.","Branch handler logic between HTML and non-HTML endpoints.","Remember linkedom parses only the initial HTML; it cannot recover a DOM from JSON or binary responses."],"tags":["dom","link-extraction","crawler","html-parsing"],"backgroundTag":"dom-not-available","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}