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

When a request has skipNavigation set, the crawler never navigates, so request.loadedUrl is never populated. prepareNavigation wraps the request in a Proxy that throws NavigationSkippedError if loadedUrl is accessed, making the absence explicit.

Source

Thrown at packages/browser-crawler/src/internals/browser-crawler.ts:639

                const urls = await extractLinks(options);

                return addRequests(urls, {
                    ...options,
                    baseUrl,
                    strategy: options.strategy ?? EnqueueStrategy.SameHostname,
                });
            },
        };
    }

    private async prepareNavigation(crawlingContext: Context): Promise<Partial<Context>> {
        if (crawlingContext.request.skipNavigation) {
            return {
                request: new Proxy(crawlingContext.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<Request>,
                get response(): Response {
                    throw new NavigationSkippedError(
                        'The `response` property is not available - `skipNavigation` was used',
                    );
                },
            } as Partial<Context>;
        }

        crawlingContext.request.state = RequestState.BEFORE_NAV;

        return {
            // Default to the full navigation timeout so a pre-navigation hook can read it; `navigate` narrows it

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Don't read request.loadedUrl when skipNavigation is set; use request.url instead
  2. Guard with a check: if (!request.skipNavigation) access loadedUrl
  3. Remove skipNavigation if you actually need navigation and a loadedUrl

Example fix

// before
const url = ctx.request.loadedUrl;
// after
const url = ctx.request.skipNavigation ? ctx.request.url : ctx.request.loadedUrl;
Defensive patterns

Strategy: type-guard

Validate before calling

const url = ctx.request.skipNavigation ? ctx.request.url : ctx.request.loadedUrl ?? ctx.request.url;

Type guard

function hasLoadedUrl(req: Request): boolean {
    return !req.skipNavigation && typeof (req as LoadedRequest).loadedUrl === 'string';
}

Try / catch

let loadedUrl: string;
try { loadedUrl = ctx.request.loadedUrl; } catch (e) { if (/skipNavigation/.test(String(e.message))) loadedUrl = ctx.request.url; else throw e; }

Prevention

When it happens

Trigger: Adding a request with skipNavigation: true and then reading request.loadedUrl in the request handler or a hook.

Common situations: Requests used purely to trigger side-effect handlers (API calls, uploads) without navigation; users assuming loadedUrl is always set after a request is processed.

Related errors


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