apify/crawlee · error · NavigationSkippedError

The `document` property is not available - `skipNavigation`

Error message

The `document` property is not available - `skipNavigation` was used

What it means

Same skipNavigation mechanism: `context.document` normally returns the JSDOM `window.document`, but with navigation skipped no document exists, so the getter throws NavigationSkippedError to prevent silent `undefined` DOM operations.

Source

Thrown at packages/jsdom-crawler/src/internals/jsdom-crawler.ts:377

                },
            };
        } 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 }) {
        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.');
            }

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Remove `skipNavigation: true` from the request/crawler options.
  2. Parse `context.body` with cheerio instead of using document.querySelector.
  3. Route DOM-requiring requests to a non-skipNavigation crawler variant.
  4. Check `request` options at the top of the handler and bail out before DOM access.

Example fix

// before (skipNavigation: true)
const title = context.document.querySelector('title')?.textContent;

// after
const cheerio = await import('cheerio');
const $ = cheerio.load(context.body);
const title = $('title').text();
Defensive patterns

Strategy: try-catch

Validate before calling

if (request.skipNavigation === true) {
    throw new Error('This handler requires a document; disable skipNavigation for this request');
}

Type guard

function hasDocument(ctx: object): ctx is { document: Document } {
    return 'document' in ctx && (ctx as any).document !== undefined;
}

Try / catch

let $: cheerio.CheerioAPI | null = null;
try {
    const doc = context.document;
    $ = loadFromDocument(doc);
} catch (err) {
    if ((err as Error).message.includes('`skipNavigation` was used')) {
        $ = (await import('cheerio')).load(context.body);
    } else {
        throw err;
    }
}

Prevention

When it happens

Trigger: Accessing `context.document` (e.g. `context.document.querySelector(...)`) inside a JSDOMCrawler handler running with `skipNavigation: true`.

Common situations: Parsing pages with document APIs after enabling skipNavigation for performance; running preNavigationHooks/postNavigationHooks that use document; shared crawlers where some request options set skipNavigation dynamically.

Related errors


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