apify/crawlee · error · Error
Selector '${selector}' not found.
Error message
Selector '${selector}' not found. What it means
CheerioCrawler's `waitForSelector` helper is synchronous over the already-parsed Cheerio document; it cannot wait. If the selector matches zero elements at call time it throws 'Selector not found', unlike browser-based crawlers where it would poll until timeout.
Source
Thrown at packages/cheerio-crawler/src/internals/cheerio-crawler.ts:301
enqueueLinks: async (options: EnqueueLinksOptions = {}) => {
const baseUrl = resolveBaseUrlForEnqueueLinksFiltering({
enqueueStrategy: options.strategy,
finalRequestUrl: crawlingContext.request.loadedUrl,
originalRequestUrl: crawlingContext.request.url,
userProvidedBaseUrl: options.baseUrl,
});
const urls = await extractLinks(options);
return addRequests(urls, {
...options,
baseUrl,
strategy: options.strategy ?? EnqueueStrategy.SameHostname,
});
},
waitForSelector: async (selector: string, _timeoutMs?: number) => {
if (crawlingContext.$(selector).get().length === 0) {
throw new Error(`Selector '${selector}' not found.`);
}
},
parseWithCheerio: async (selector?: string, timeoutMs?: number) => {
if (selector) {
await crawlingContext.waitForSelector(selector, timeoutMs);
}
return crawlingContext.$;
},
};
}
}
/**
* Creates new {@apilink Router} instance that works based on request labels.
* This instance can then serve as a `requestHandler` of your {@apilink CheerioCrawler}.
* Defaults to the {@apilink CheerioCrawlingContext}.
*View on GitHub (pinned to dbe57fb09c)
Solutions
- Fix the selector or verify the element truly exists in the served HTML.
- Remove waitForSelector calls in CheerioCrawler — it does not wait; the document is already fully parsed.
- If content is client-rendered, switch to PlaywrightCrawler/PuppeteerCrawler.
- Check `$(selector).length` yourself before calling to avoid the throw.
Example fix
// before
await context.waitForSelector('div.results');
// after
if (context.$('div.results').length === 0) { log.warning('no results in static HTML'); } else { await context.parseWithCheerio('div.results'); } Defensive patterns
Strategy: validation
Validate before calling
if (context.$('div.results').length === 0) { log.warning('selector missing in static HTML'); return; } Type guard
function selectorExists(ctx: { $: CheerioAPI }, selector: string): boolean { return ctx.$(selector).length > 0; } Try / catch
try { await context.waitForSelector(sel); } catch (err) { if (err.message.startsWith("Selector '")) { /* static HTML lacks element */ } else { throw err; } } Prevention
- Remember CheerioCrawler's waitForSelector never waits — remove such calls when porting from browser crawlers
- Inspect the served HTML to confirm selectors match server-rendered content
- Use PlaywrightCrawler for client-rendered content
When it happens
Trigger: Calling `waitForSelector(selector)` (directly or via `parseWithCheerio(selector)`) when the loaded page's Cheerio DOM contains no elements matching the selector.
Common situations: Porting code from PlaywrightCrawler/PuppeteerCrawler where waitForSelector waits for dynamically injected content; typos in selectors; content rendered client-side that Cheerio's static HTML never sees.
Related errors
- The `body` property is not available - `skipNavigation` was
- The `$` property is not available - `skipNavigation` was use
- Cannot extract links because the DOM is not available.
- Selector '${selector}' not found.
- Selector '${selector}' not found.
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/39a425900d666016.
Report an issue: GitHub.