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
- Remove `skipNavigation` so the response is parsed into `$` before enqueueLinks runs.
- Move `enqueueLinks` to a handler path that only runs for HTML responses.
- 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
- Only call enqueueLinks from handlers where the response was parsed
- Check `context.$` truthiness before link extraction
- Avoid skipNavigation on crawlers that need to follow discovered links
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
- An extracted URL: ${href} is relative and options.baseUrl is
- The `body` property is not available - `skipNavigation` was
- The `$` property is not available - `skipNavigation` was use
- Selector '${selector}' not found.
- Cannot parse Glob pattern '${globTrimmed}': it must be an no
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/7366e6655de9e95f.
Report an issue: GitHub.