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
- Enable navigation (remove `skipNavigation: true`) for requests where you need enqueueLinks/extractLinks.
- Extract links from the raw HTML yourself: cheerio.load(context.body) and collect `a[href]`.
- Skip the enqueueLinks call when window is unavailable and log a warning instead.
- 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
- Only call enqueueLinks on requests that produced a JSDOM window.
- Implement a cheerio-based link extractor as the standard fallback path.
- Keep skipNavigation crawls and link-discovery crawls as separate pipeline stages.
- Check `context.window` before any helper that needs the DOM.
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
- The `window` property is not available - `skipNavigation` wa
- The `body` property is not available - `skipNavigation` was
- The `document` property is not available - `skipNavigation`
- The `window` property is not available - `skipNavigation` wa
- The `body` property is not available - `skipNavigation` was
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/43ee2cabd4db087f.
Report an issue: GitHub.