apify/crawlee · error · Error

Cannot extract links because the JSDOM is not available.

Error message

Cannot extract links because the JSDOM is not available.

What it means

extractLinks() (and therefore enqueueLinks()) relies on the JSDOM `window` to run querySelectorAll over the parsed document. When the crawling context has no window — typically because navigation was skipped — link extraction is impossible and the error is thrown explicitly instead of returning an empty list.

Source

Thrown at packages/jsdom-crawler/src/internals/jsdom-crawler.ts:394

                    get document(): Document {
                        throw new NavigationSkippedError(
                            'The `document` property is not available - `skipNavigation` was used',
                            { cause: err },
                        );
                    },
                };
            }

            throw err;
        }
    }

    private async addHelpers(crawlingContext: InternalHttpCrawlingContext & { body: string; window: DOMWindow }) {
        const addRequests = crawlingContext.addRequests;

        const extractLinks = async (options?: ExtractLinksOptions): Promise<string[]> => {
            if (!crawlingContext.window) {
                throw new Error('Cannot extract links because the JSDOM is not available.');
            }

            return extractUrlsFromWindow(
                crawlingContext.window,
                options?.selector ?? 'a',
                options?.baseUrl ?? crawlingContext.request.loadedUrl ?? crawlingContext.request.url,
            );
        };

        return {
            extractLinks,
            enqueueLinks: async (options: EnqueueLinksOptions = {}) => {
                const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
                    enqueueStrategy: options.strategy,
                    finalRequestUrl: crawlingContext.request.loadedUrl,
                    originalRequestUrl: crawlingContext.request.url,
                    userProvidedBaseUrl: options.baseUrl,
                });

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Enable navigation (remove `skipNavigation: true`) for requests where you need enqueueLinks/extractLinks.
  2. Extract links from the raw HTML yourself: cheerio.load(context.body) and collect `a[href]`.
  3. Skip the enqueueLinks call when window is unavailable and log a warning instead.
  4. Use CheerioCrawler + enqueueLinks for link discovery on skipNavigation-style crawls.

Example fix

// before (fails with skipNavigation)
await context.enqueueLinks();

// after
if (context.window) {
    await context.enqueueLinks();
} else {
    const $ = (await import('cheerio')).load(context.body);
    const urls = $('a[href]').map((_, el) => $(el).attr('href')).get();
    await context.addRequests(urls.map((u) => ({ url: new URL(u, context.request.loadedUrl).href })));
}
Defensive patterns

Strategy: fallback

Validate before calling

if (!context.window) {
    // cannot call enqueueLinks/extractLinks; use cheerio fallback on context.body
}

Type guard

function canExtractLinks(ctx: { window?: unknown }): ctx is { window: DOMWindow } {
    return Boolean((ctx as any).window);
}

Try / catch

try {
    await context.enqueueLinks();
} catch (err) {
    if ((err as Error).message.includes('JSDOM is not available')) {
        const $ = (await import('cheerio')).load(context.body);
        const urls = $('a[href]').map((_, el) => $(el).attr('href')!).get()
            .map((u) => new URL(u, context.request.loadedUrl).href);
        await context.addRequests(urls.map((url) => ({ url })));
    } else {
        throw err;
    }
}

Prevention

When it happens

Trigger: Calling `context.enqueueLinks()` or `context.extractLinks()` in a JSDOMCrawler request that ran with `skipNavigation: true`, or any path where `crawlingContext.window` is falsy.

Common situations: enable enqueuing of links in a fast skipNavigation crawl; handlers reused across normal and skipNavigation requests.

Related errors


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