apify/crawlee · error · NavigationSkippedError
The `body` property is not available - `skipNavigation` was
Error message
The `body` property is not available - `skipNavigation` was used
What it means
In skipNavigation mode the response is never loaded into a DOM, so even the convenience `body` getter is replaced by a throwing getter. Unlike the normal path (where `body` returns document.documentElement.outerHTML), skipNavigation requests expose no parsed representation through this context object.
Source
Thrown at packages/jsdom-crawler/src/internals/jsdom-crawler.ts:371
window,
get body() {
return window.document.documentElement.outerHTML;
},
get document() {
return window.document;
},
};
} catch (err) {
if (err instanceof NavigationSkippedError) {
return {
get window(): DOMWindow {
throw new NavigationSkippedError(
'The `window` property is not available - `skipNavigation` was used',
{ cause: err },
);
},
get body(): string {
throw new NavigationSkippedError(
'The `body` property is not available - `skipNavigation` was used',
{ cause: err },
);
},
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 }) {View on GitHub (pinned to dbe57fb09c)
Solutions
- Disable `skipNavigation` so the JSDOM is created and `body` is populated.
- If skipNavigation is required, consume the raw response via `context.response` / fetch results instead of `context.body`.
- Use CheerioCrawler for such requests if only HTML string parsing is needed.
- Guard handler code to only read `body` when navigation was not skipped.
Example fix
// before (skipNavigation: true)
const { window } = context;
const html = context.body;
// after
const html = context.response?.body ?? ''; // raw bytes from the response, not the DOM body getter Defensive patterns
Strategy: try-catch
Validate before calling
if (request.skipNavigation === true) {
// `context.body` getter will throw; use context.response instead
} Type guard
function canReadDomBody(ctx: { body?: unknown }): ctx is { body: string } {
return ctx.body !== undefined;
} Try / catch
let html: string;
try {
html = context.body;
} catch (err) {
if ((err as Error).message.includes('`skipNavigation` was used')) {
html = await context.response.text(); // raw source fallback
} else {
throw err;
}
} Prevention
- In skipNavigation mode, read the response object, not DOM properties.
- Document in handler comments that `body`/`window`/`document` getters throw under skipNavigation.
- Prefer CheerioCrawler for raw-HTML-only workloads.
- Type your context narrowly so DOM getters are not part of the skipNavigation path.
When it happens
Trigger: Reading `context.body` in a JSDOMCrawler requestHandler while the request was run with `skipNavigation: true`.
Common situations: Handlers that default to using `context.body` for HTML parsing but are attached to a crawler instance configured with skipNavigation; copy-pasted handlers between JSDOMCrawler and CheerioCrawler (CheerioCrawler's `body` works fine).
Related errors
- The `window` property is not available - `skipNavigation` wa
- The `document` property is not available - `skipNavigation`
- Cannot extract links because the JSDOM is not available.
- 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/29d99044bad58266.
Report an issue: GitHub.