{"record":{"id":"44114f2f7b62e4d7","repo":"apify/crawlee","slug":"selector-selector-not-found-44114f","errorCode":null,"errorMessage":"Selector '${selector}' not found.","messagePattern":"Selector '(.+?)' not found\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/jsdom-crawler/src/internals/jsdom-crawler.ts","lineNumber":433,"sourceCode":"\n                return addRequests(urls, {\n                    ...options,\n                    baseUrl,\n                    strategy: options.strategy ?? EnqueueStrategy.SameHostname,\n                });\n            },\n            async waitForSelector(selector: string, timeoutMs = 5_000) {\n                const cheerio = await import('cheerio');\n                const $ = cheerio.load(crawlingContext.body);\n\n                if ($(selector).get().length === 0) {\n                    if (timeoutMs) {\n                        await sleep(50);\n                        await this.waitForSelector(selector, Math.max(timeoutMs - 50, 0));\n                        return;\n                    }\n\n                    throw new Error(`Selector '${selector}' not found.`);\n                }\n            },\n            async parseWithCheerio(selector?: string, _timeoutMs = 5_000) {\n                const cheerio = await import('cheerio');\n                const $ = cheerio.load(crawlingContext.body);\n\n                if (selector && $(selector).get().length === 0) {\n                    throw new Error(`Selector '${selector}' not found.`);\n                }\n\n                return $;\n            },\n        };\n    }\n}\n\n/**\n * Extracts URLs from a given Window object.","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/apify/crawlee/blob/dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c/packages/jsdom-crawler/src/internals/jsdom-crawler.ts#L415-L451","documentation":"JSDOMCrawler's waitForSelector is implemented with cheerio over the static body; it polls every 50ms until the timeout expires. If the selector still matches nothing when timeoutMs reaches 0, it throws 'Selector ... not found.' — it never re-fetches the page, so the selector must exist in the already-loaded HTML.","triggerScenarios":"Calling `context.waitForSelector(selector)` with a selector absent from the parsed body, or with `timeoutMs = 0` for an element not present immediately; using selectors that only appear after client-side JS execution not reflected in the served HTML.","commonSituations":"Waiting for SPA-rendered elements while crawling server-rendered HTML; typos in selectors; elements injected by runScripts but the crawl raced the window load; passing timeout 0 expecting infinite wait.","solutions":["Verify the selector exists in the fetched HTML (log `context.body` or test with cheerio).","Enable `runScripts: true` (or wait for window load) if the element is created by client-side JS.","Increase timeoutMs, but remember the HTML is static — a longer wait only helps if scripts mutate the DOM.","Fix selector typos and confirm the correct case-sensitive attribute syntax."],"exampleFix":"// before\nawait context.waitForSelector('.dynamic-item', 0); // throws immediately if absent\n\n// after\nawait context.waitForSelector('.dynamic-item', 5000); // poll up to 5s (JS must populate DOM)","handlingStrategy":"try-catch","validationCode":"const $ = (await import('cheerio')).load(context.body);\nif ($(selector).get().length === 0) {\n    logger.warning(`Selector ${selector} not in served HTML; waitForSelector would exhaust timeout`);\n}","typeGuard":"null","tryCatchPattern":"try {\n    await context.waitForSelector('.item', 5000);\n} catch (err) {\n    if ((err as Error).message.includes(\"not found\")) {\n        logger.warning('Element never appeared in static HTML', { selector: '.item' });\n        return; // skip request or fallback parsing\n    }\n    throw err;\n}","preventionTips":["Verify selectors against a saved copy of the HTML before adding them to production code.","Remember the DOM is static — enable runScripts if JS must render the element.","Use a positive timeout (not 0) when the element may appear via scripts.","Detect bot-block/error pages (empty selectors) and treat them as request failures."],"tags":["jsdom","selector","cheerio","timeout"],"backgroundTag":"selector-not-found","analyzedSha":"dbe57fb09ca607ad59dcf998f3925ef9ac3bb26c","analyzedAt":"2026-08-30T22:22:28.328Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}