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.
 * @ignore

View on GitHub (pinned to dbe57fb09c)

Solutions

  1. Verify the selector against the actual HTML (`document.querySelector` in the browser devtools or by logging `context.body`).
  2. Remember linkedom does not execute JavaScript: if the element is rendered client-side, use PlaywrightCrawler instead of LinkedomCrawler.
  3. Catch the error and treat the element as optional, or wrap waitForSelector in try/catch with a fallback parse.
  4. 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

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


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