apify/crawlee · error · Error

Cannot extract links because the DOM is not available.

Error message

Cannot extract links because the DOM is not available.

What it means

CheerioCrawler's `extractLinks` helper (backing `enqueueLinks`) needs a parsed DOM (`$`) to select link elements. If the crawling context has no `$` — e.g. skipNavigation was used or the response was not HTML — the helper throws immediately instead of scanning an empty document.

Source

Thrown at packages/cheerio-crawler/src/internals/cheerio-crawler.ts:271

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

            throw err;
        }
    }

    private async addHelpers(crawlingContext: InternalHttpCrawlingContext & { $: CheerioAPI }) {
        const addRequests = crawlingContext.addRequests;

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

            return extractUrlsFromCheerio(
                crawlingContext.$,
                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. Remove `skipNavigation` so the response is parsed into `$` before enqueueLinks runs.
  2. Move `enqueueLinks` to a handler path that only runs for HTML responses.
  3. Enqueue requests manually via `addRequests` with URLs derived from `request.url` or the API response instead of DOM extraction.

Example fix

// before
new CheerioCrawler({ skipNavigation: true, requestHandler: async ({ enqueueLinks }) => { await enqueueLinks(); } });
// after
new CheerioCrawler({ requestHandler: async ({ enqueueLinks }) => { await enqueueLinks(); } });
Defensive patterns

Strategy: type-guard

Validate before calling

if (!('$' in context) || !context.$) { log.warning('enqueueLinks skipped: no DOM'); return; }

Type guard

function hasDom(ctx: CrawlingContext): ctx is CrawlingContext & { $: CheerioAPI } { return '$' in ctx && Boolean((ctx as any).$); }

Try / catch

try { await enqueueLinks(); } catch (err) { if (err.message.includes('DOM is not available')) { await context.addRequests([{ url: alternateUrl }]); } else { throw err; } }

Prevention

When it happens

Trigger: Calling `enqueueLinks()` / `extractLinks()` in a handler where `crawlingContext.$` is undefined, typically with `skipNavigation: true` or a non-HTML response.

Common situations: Enabling skipNavigation on a crawler that also enqueues links; running enqueueLinks on binary/JSON endpoints where Cheerio parsing was skipped.

Related errors


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