apify/crawlee · error · NavigationSkippedError
The `window` property is not available - `skipNavigation` wa
Error message
The `window` property is not available - `skipNavigation` was used
What it means
When the page handler ran with `skipNavigation: true`, the JSDOMCrawler context intentionally does not parse the response into a JSDOM window. The returned object replaces the `window` getter with one that throws NavigationSkippedError to make it obvious that DOM APIs are unavailable in skipNavigation mode.
Source
Thrown at packages/jsdom-crawler/src/internals/jsdom-crawler.ts:365
} catch (e) {
this.log.debug((e as Error).message);
}
}
return {
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 },
);
},
};
}View on GitHub (pinned to dbe57fb09c)
Solutions
- Remove `skipNavigation: true` if you actually need DOM access.
- Access `context.body` (raw response text) instead of `window` and parse manually.
- Branch your handler on whether navigation was skipped before touching `window`.
- Move the DOM-dependent logic to a crawler configured without skipNavigation.
Example fix
// before
async function handler(context) {
const doc = context.window.document;
}
// after with skipNavigation
async function handler(context) {
const html = context.body; // raw string, no JSDOM window
const $ = (await import('cheerio')).load(html);
} Defensive patterns
Strategy: try-catch
Validate before calling
const navigationSkipped = request.skipNavigation === true;
if (navigationSkipped) {
// do not touch context.window
} Type guard
function isNavigationSkippedError(err: unknown): err is Error & { name: 'NavigationSkippedError' } {
return err instanceof Error && err.name === 'NavigationSkippedError';
} Try / catch
try {
const { window } = context;
process(window);
} catch (err) {
if ((err as Error).message.includes('`skipNavigation` was used')) {
return handleRawBody(context.body);
}
throw err;
} Prevention
- Keep two handler variants: one for skipNavigation requests, one for DOM parsing.
- Never share DOM-accessing handler code with skipNavigation-enabled crawlers without a guard.
- Encode skipNavigation in request userData and branch on it at handler start.
- Add an integration test covering a skipNavigation request through your handler.
When it happens
Trigger: Using JSDOMCrawler with `skipNavigation: true` (or `skipNavigation()` in the hook flow) and then reading `context.window` in the requestHandler.
Common situations: Reusing a shared handler that normally parses DOM after switching some requests to skipNavigation to save resources; forgetting that skipNavigation only gives access to raw `body` bytes.
Related errors
- The `body` property is not available - `skipNavigation` was
- 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/fb8fb19677613d4f.
Report an issue: GitHub.