{"record":{"id":"5fa7a5a16f6fadf0","repo":"jackwener/OpenCLI","slug":"booking-com-page-declared-results-but-no-property","errorCode":null,"errorMessage":"Booking.com page declared results but no property cards were parsed: ${totalText}","messagePattern":"Booking\\.com page declared results but no property cards were parsed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/booking/search.js","lineNumber":298,"sourceCode":"    if (!raw || typeof raw !== 'object') {\n      throw new CommandExecutionError('Booking.com page returned no extractable data');\n    }\n    if (raw.blocked) {\n      throw new CommandExecutionError('Booking.com served a verification / captcha page; retry later or change profile');\n    }\n\n    if (raw.ok !== true) {\n      throw new CommandExecutionError('Booking.com extractor returned an invalid status');\n    }\n    if (!Array.isArray(raw.items)) {\n      throw new CommandExecutionError('Booking.com extractor returned malformed items');\n    }\n\n    const items = raw.items;\n    if (items.length === 0) {\n      const totalText = String(raw.totalText || '').trim();\n      if (hasPositiveResultCount(totalText)) {\n        throw new CommandExecutionError(\n          `Booking.com page declared results but no property cards were parsed: ${totalText}`,\n        );\n      }\n      throw new EmptyResultError(\n        `booking search ${JSON.stringify(destination)}`,\n        totalText\n          ? `No hotels rendered (${totalText}). Try a broader destination, different dates, or check the URL in a browser.`\n          : 'No hotels rendered. Try a broader destination, different dates, or check the URL in a browser.',\n      );\n    }\n\n    return items.slice(0, limit).map((it, i) => {\n      if (!it || typeof it !== 'object') {\n        throw new CommandExecutionError('Booking.com extractor returned malformed hotel row');\n      }\n      const name = String(it.name || '').trim();\n      const country = String(it.country || '').trim();\n      const slug = String(it.slug || '').trim();","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/booking/search.js#L280-L316","documentation":"This CommandExecutionError is thrown when the extractor returned an empty items array but the page's own result-count text (raw.totalText) indicates a positive number of results. It distinguishes 'genuinely no results' (which raises EmptyResultError, see 528) from 'results exist but parsing failed' — a scraping/selector mismatch.","triggerScenarios":"items.length === 0 AND hasPositiveResultCount(totalText) is true — e.g. totalText like '1,024 properties found' while zero property cards were parsed from the DOM.","commonSituations":"Booking.com changed its card markup (data-testid renamed) so the selector matches nothing; page was still lazy-loading cards when extraction ran despite the 20s wait; results rendered inside a different container (map view, list-vs-grid toggle); partial bot-block that kept the header but replaced cards.","solutions":["Update the library so the property-card selector matches Booking.com's current markup.","Retry — cards may not have finished lazy-loading; allow a longer settle time before extraction.","Check whether the page rendered a non-default view (map/grid) and force the standard list view in the URL.","Open the same URL in a regular browser to confirm cards are present, then compare DOM.","If a captcha/verification overlay appeared, switch profile/IP (related to blocked detection)."],"exampleFix":"// before (wait once for first card)\nawait page.wait('selector', '[data-testid=property-card]', { timeoutMs: 20000 }).catch(() => {});\n// after (also wait for more cards to lazy-load)\nawait page.wait('selector', '[data-testid=property-card]', { timeoutMs: 20000 }).catch(() => {});\nawait page.evaluate(() => window.scrollBy(0, 2000));\nawait new Promise(r => setTimeout(r, 3000));","handlingStrategy":"retry","validationCode":"// check the URL will render the standard list view\nconst url = new URL(searchUrl);\nif (url.searchParams.get('nflt') && url.searchParams.get('nflt').includes('view')) console.warn('view filters may hide property cards');","typeGuard":null,"tryCatchPattern":"try {\n  return await booking.search(params);\n} catch (e) {\n  if (/declared results but no property cards were parsed/.test(e.message)) {\n    await sleep(5000);            // let lazy-load finish\n    return booking.search(params);\n  }\n  throw e;\n}","preventionTips":["Keep the library's card selector in sync with Booking.com DOM updates.","Allow extra settle/scroll time for lazy-loaded price cells.","Force the default list view in the search URL.","Verify in a real browser that the same URL shows property cards.","Distinguish this from EmptyResultError before changing your query — results DO exist here."],"tags":["scraping","selector-mismatch","browser-automation"],"backgroundTag":"selector-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}