apify/crawlee · error · NavigationSkippedError
The `$` property is not available - `skipNavigation` was use
Error message
The `$` property is not available - `skipNavigation` was used
What it means
Same mechanism as `body`: with `skipNavigation` used, the response is not parsed, so the `$` (CheerioAPI) property is a getter that throws NavigationSkippedError. Crawlee throws instead of returning a broken/undefined parser so the mistake fails loudly.
Source
Thrown at packages/cheerio-crawler/src/internals/cheerio-crawler.ts:254
const $ = cheerio.load(dom, {
xml: { decodeEntities: true, xmlMode: isXml },
} as CheerioOptions);
return {
$,
body,
};
} catch (err) {
if (err instanceof NavigationSkippedError) {
return {
get body(): string {
throw new NavigationSkippedError(
'The `body` property is not available - `skipNavigation` was used',
{ cause: err },
);
},
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.');
}View on GitHub (pinned to dbe57fb09c)
Solutions
- Remove all `$(...)` usage from handlers running with skipNavigation.
- Disable `skipNavigation` if DOM parsing is required.
- Branch inside the handler on the crawler configuration instead of touching `$` unconditionally.
Example fix
// before
new CheerioCrawler({ skipNavigation: true, requestHandler: async ({ $ }) => { await $('h1').text(); } });
// after
new CheerioCrawler({ skipNavigation: true, requestHandler: async ({ request }) => { log.info(request.url); } }); Defensive patterns
Strategy: validation
Validate before calling
if (crawlerOptions.skipNavigation && handlerSource.includes('$(')) { throw new Error('handler uses $ but skipNavigation is enabled'); } Type guard
function hasCheerio(ctx: CrawlingContext): ctx is CrawlingContext & { $: CheerioAPI } { return (ctx as any).$ !== undefined; } Try / catch
try { const text = context.$('h1').text(); } catch (err) { if (err instanceof NavigationSkippedError) { log.info('no DOM, skipping parse'); } else { throw err; } } Prevention
- Grep handlers for `$(` when toggling skipNavigation on
- Type handlers narrowly so $ is not in the context type when skipNavigation is set
- Use PlaywrightCrawler when DOM access is required
When it happens
Trigger: Accessing `crawlingContext.$` (or destructuring it) in a requestHandler while `skipNavigation: true` is set on the CheerioCrawler or the specific request.
Common situations: Using `$('a').each(...)` or `enqueueLinks` style logic in a handler after enabling skipNavigation; shared request handler reused by both a parsing crawler and a navigation-only crawler.
Related errors
- The `body` property is not available - `skipNavigation` was
- 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
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/c39c5e5aee160579.
Report an issue: GitHub.