apify/crawlee · error · NavigationSkippedError

The `request.loadedUrl` property is not available - `skipNav

Error message

The `request.loadedUrl` property is not available - `skipNavigation` was used

What it means

NavigationSkippedError thrown by a Proxy over `request` in the crawling context. When a request was created with `skipNavigation: true`, no navigation (and thus no loaded URL) ever happened, so accessing `request.loadedUrl` throws instead of returning a misleading `undefined`.

Source

Thrown at packages/http-crawler/src/internals/http-crawler.ts:506

        for (const hook of this.#postNavigationHooks) {
            pipelineWithNavigation = pipelineWithNavigation.compose(windowGuard(hook));
        }

        return pipelineWithNavigation
            .compose({ action: this.processHttpResponse.bind(this) })
            .compose({ action: this.handleBlockedRequestByContent.bind(this) });
    }

    private async prepareHttpRequest(crawlingContext: CrawlingContext): Promise<Partial<CrawlingContextWithResponse>> {
        const { request } = crawlingContext;

        if (request.skipNavigation) {
            return {
                request: new Proxy(request, {
                    get(target, propertyName, receiver) {
                        if (propertyName === 'loadedUrl') {
                            throw new NavigationSkippedError(
                                'The `request.loadedUrl` property is not available - `skipNavigation` was used',
                            );
                        }
                        return Reflect.get(target, propertyName, receiver);
                    },
                }) as LoadedRequest<CrawleeRequest>,
                get response(): InternalHttpCrawlingContext['response'] {
                    throw new NavigationSkippedError(
                        'The `response` property is not available - `skipNavigation` was used',
                    );
                },
            } as Partial<CrawlingContextWithResponse>;
        }

        request.state = RequestState.BEFORE_NAV;
        return {};
    }

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Use `request.url` instead of `request.loadedUrl` when navigation was skipped.
  2. Only read `loadedUrl` when `request.skipNavigation` is falsy, or when `response` exists.
  3. Restructure so skipNavigation requests use a different handler (e.g. route via labels) that never touches `loadedUrl`.

Example fix

// before
const url = context.request.loadedUrl;
// after
if (!context.request.skipNavigation) { const url = context.request.loadedUrl; } else { const url = context.request.url; }
Defensive patterns

Strategy: type-guard

Validate before calling

if (request.skipNavigation === true) { /* never touch loadedUrl */ }

Type guard

function isLoaded(ctx: { request: { skipNavigation?: boolean; loadedUrl?: string; url: string } }): ctx is { request: { skipNavigation?: false; loadedUrl: string; url: string } } { return !ctx.request.skipNavigation; }

Try / catch

try { const url = ctx.request.loadedUrl; } catch (err) { if (err instanceof NavigationSkippedError) { const url = ctx.request.url; } else throw err; }

Prevention

When it happens

Trigger: Queueing a request with `skipNavigation: true` (e.g. to seed other requests or skip fetching) and then reading `context.request.loadedUrl` inside the request handler.

Common situations: Using skipped-navigation requests as 'virtual' entries to fan out child requests; shared handler code that assumes every request was fetched; copy-pasted handlers from normal crawls reused for skipNavigation requests.

Related errors


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