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

  1. Disable `skipNavigation` so the JSDOM is created and `body` is populated.
  2. If skipNavigation is required, consume the raw response via `context.response` / fetch results instead of `context.body`.
  3. Use CheerioCrawler for such requests if only HTML string parsing is needed.
  4. 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

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


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