apify/crawlee · error
Selector '${selector}' not found.
Error message
Selector '${selector}' not found. What it means
waitForSelector polls the linkedom DOM for an element matching `selector` every 50ms until the timeout elapses. If the selector still matches nothing when the remaining timeout reaches zero, the crawler gives up and throws 'Selector ... not found.' It signals that the awaited element never appeared in the parsed document.
Source
Thrown at packages/linkedom-crawler/src/internals/linkedom-crawler.ts:315
const urls = await extractLinks(options);
return addRequests(urls, {
...options,
baseUrl,
strategy: options.strategy ?? EnqueueStrategy.SameHostname,
});
},
async waitForSelector(selector: string, timeoutMs = 5_000) {
const $ = cheerio.load(crawlingContext.body);
if ($(selector).get().length === 0) {
if (timeoutMs) {
await sleep(50);
await this.waitForSelector(selector, Math.max(timeoutMs - 50, 0));
return;
}
throw new Error(`Selector '${selector}' not found.`);
}
},
async parseWithCheerio(selector?: string, _timeoutMs = 5_000) {
const $ = cheerio.load(crawlingContext.body);
if (selector && $(selector).get().length === 0) {
throw new Error(`Selector '${selector}' not found.`);
}
return $;
},
};
}
}
/**
* Extracts URLs from a given Window object.
* @ignoreView on GitHub (pinned to dbe57fb09c)
Solutions
- Verify the selector against the actual HTML (`document.querySelector` in the browser devtools or by logging `context.body`).
- Remember linkedom does not execute JavaScript: if the element is rendered client-side, use PlaywrightCrawler instead of LinkedomCrawler.
- Catch the error and treat the element as optional, or wrap waitForSelector in try/catch with a fallback parse.
- Check for bot-protection or consent interstitials replacing the expected content and handle those pages separately.
Example fix
// before
await context.waitForSelector('.product-price');
// after
try {
await context.waitForSelector('.product-price');
} catch {
log.warning('Product price not found on page, skipping.');
return;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await context.waitForSelector('.target', 5000);
} catch (err) {
if (err.message.includes('not found')) {
log.warning('Selector .target never appeared; continuing without it.');
} else {
throw err;
}
} Prevention
- Verify selectors against the actual HTML (log context.body or check in devtools) before waiting on them.
- Do not use linkedom for pages that require JavaScript to render the awaited element; use PlaywrightCrawler.
- Treat optional elements with try/catch instead of hard waits.
- Account for consent walls / bot-protection pages that replace expected content.
When it happens
Trigger: Calling `context.waitForSelector(selector)` (with default or explicit timeout) in a LinkedomCrawler handler when the parsed body contains no element matching the selector after the full timeout.
Common situations: Waiting for client-side-rendered content that linkedom (a static DOM, no JS execution) will never produce; typos in CSS selectors; content behind login/consent walls; waiting on elements loaded by XHR after initial HTML parse.
Related errors
- Selector '${selector}' not found.
- Cannot extract links because the DOM is not available.
- The current SessionPool instance couldn't find a valid sessi
- fetchNextRequest called on an uninitialized crawler
- Navigation timed out after ${this.#navigationTimeoutMillis /
AI-assisted analysis of apify/crawlee@dbe57fb09c (2026-08-30).
Data as JSON: /api/errors/3efb7dec1fdcc4f1.
Report an issue: GitHub.