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

  1. Remove `skipNavigation: true` if you actually need DOM access.
  2. Access `context.body` (raw response text) instead of `window` and parse manually.
  3. Branch your handler on whether navigation was skipped before touching `window`.
  4. 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

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


AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30). Data as JSON: /api/errors/fb8fb19677613d4f. Report an issue: GitHub.