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
- Use `request.url` instead of `request.loadedUrl` when navigation was skipped.
- Only read `loadedUrl` when `request.skipNavigation` is falsy, or when `response` exists.
- 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
- Never read loadedUrl for skipNavigation requests.
- Prefer request.url for identity; loadedUrl only reflects a fetched response.
- Route skipped requests to dedicated handlers by label.
- Write shared helpers that accept an explicit url parameter instead of reading context.
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
- The `response` property is not available - `skipNavigation`
- The `contentType` property is not available - `skipNavigatio
- The `body` property is not available - `skipNavigation` was
- The `json` property is not available - `skipNavigation` was
- The `waitForSelector` method is not available - `skipNavigat
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/3433d008f709189a.
Report an issue: GitHub.