{"record":{"id":"4250498ed90634b2","repo":"jackwener/OpenCLI","slug":"google-images-returned-no-extractable-result-rows","errorCode":null,"errorMessage":"google images returned no extractable result rows; the page layout may have changed.","messagePattern":"google images returned no extractable result rows; the page layout may have changed\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/google/images.js","lineNumber":449,"sourceCode":"        const pageSize = Math.max(limit, 20);\n        const url = `https://www.google.com/search?tbm=isch&q=${encodeURIComponent(query)}&hl=${lang}&num=${pageSize}`;\n\n        await runBrowserStep('google images navigation', () => navigateGoogleImages(page, url));\n        try {\n            await page.wait({ selector: '#rso img, #islrg img, #center_col img, #rcnt img', timeout: 8 });\n        }\n        catch {\n            await page.wait(2).catch(() => {});\n        }\n\n        const rows = await runBrowserStep('google images extraction', () => evaluateGoogleImageRows(page, limit, resolveOriginal));\n        if (rows.length === 0) {\n            const state = await runBrowserStep('google images page-state inspection', () => evaluateGoogleImagesPageState(page));\n            if (state.captchaOrConsent) {\n                throw new CommandExecutionError('google images is blocked by a Google CAPTCHA/consent/interstitial page.');\n            }\n            if (!state.explicitNoResults) {\n                throw new CommandExecutionError('google images returned no extractable result rows; the page layout may have changed.');\n            }\n        }\n        return normalizeImageRows(rows, query, limit);\n    },\n});\n\nexport const __test__ = { command, evaluateGoogleImageRows, extractGoogleImageRows, inspectGoogleImagesPage, normalizeImageRows, navigateGoogleImages };\n","sourceCodeStart":431,"sourceCodeEnd":457,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/google/images.js#L431-L457","documentation":"This CommandExecutionError is thrown when the google images command extracts zero result rows and the page-state inspection finds no CAPTCHA/consent and no explicit 'no results' indicator. It signals that Google's DOM layout likely changed so the row-extraction selectors no longer match anything.","triggerScenarios":"evaluateGoogleImageRows returns an empty array, evaluateGoogleImagesPageState reports captchaOrConsent=false and explicitNoResults=false — i.e. an unrecognized page structure, typically after a Google Images markup change or an incomplete page load.","commonSituations":"Google ships a new Images DOM and the scraper selectors become stale; the results page is only partially rendered because scripts didn't finish before evaluate ran; locale variants of Google Images use different markup; network flakiness leaves a blank shell page.","solutions":["Re-run the command once — transient slow loads can produce an empty extraction before render completes","Update the extraction selectors in evaluateGoogleImageRows to match the current Google Images DOM","Check Google's page manually in a browser to confirm whether results exist for the query","Add a wait/retry for result containers to appear before declaring the layout changed"],"exampleFix":"// before\nconst rows = await evaluateGoogleImageRows(page, limit, resolveOriginal);\n// after\nawait page.waitForSelector('[data-attrid], img[src^=\"http\"]', {timeout: 10000}).catch(() => {});\nconst rows = await evaluateGoogleImageRows(page, limit, resolveOriginal);","handlingStrategy":"retry","validationCode":"// no caller-side validation; ensure the page is fully loaded before invoking\nawait page.waitForLoadState('networkidle');","typeGuard":"function hasResults(wrapper) {\n  return wrapper != null && Array.isArray(wrapper.items) && wrapper.items.length > 0;\n}","tryCatchPattern":"try {\n  const rows = await imagesCommand.func(args);\n} catch (e) {\n  if (e.message.includes('layout may have changed')) {\n    await page.reload({waitUntil: 'networkidle'});\n    return imagesCommand.func(args); // one retry after reload\n  }\n  throw e;\n}","preventionTips":["Wait for network-idle or result selectors before extraction","Keep scraper selectors updated against Google DOM changes","Retry once on transient render failures","Pin/verify scraper compatibility after Google frontend updates"],"tags":["scraping","dom-layout","google","selector-drift"],"backgroundTag":"scraper-selector-drift","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}