{"record":{"id":"b46b6784e4c550c4","repo":"jackwener/OpenCLI","slug":"command-selector-drift-reason","errorCode":null,"errorMessage":"${command} selector drift${reason}","messagePattern":"(.+?) selector drift(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/dribbble/utils.js","lineNumber":76,"sourceCode":"    }\n    if (!/(^|\\.)dribbble\\.com$/i.test(url.hostname)) {\n        throw new ArgumentError('shot URL must use dribbble.com');\n    }\n    const match = url.pathname.match(/^\\/shots\\/(\\d+)(?:-|\\/|$)/);\n    if (!match) throw new ArgumentError('shot URL must match dribbble.com/shots/<id>');\n    return match[1];\n}\n\nexport function requireRows(payload, command) {\n    if (!payload || typeof payload !== 'object') {\n        throw new CommandExecutionError(`${command} returned an unreadable browser payload`);\n    }\n    if (payload.empty) {\n        throw new EmptyResultError(command, payload.reason || `${command} returned no results`);\n    }\n    if (!payload.ok) {\n        const reason = payload.reason ? `: ${payload.reason}` : '';\n        throw new CommandExecutionError(`${command} selector drift${reason}`);\n    }\n    if (!Array.isArray(payload.rows)) {\n        throw new CommandExecutionError(`${command} returned a malformed rows payload`);\n    }\n    if (payload.rows.length === 0) {\n        throw new EmptyResultError(command, `${command} page loaded but no matching rows were found`);\n    }\n    return payload.rows;\n}\n\nexport function requireRow(payload, command) {\n    if (!payload || typeof payload !== 'object') {\n        throw new CommandExecutionError(`${command} returned an unreadable browser payload`);\n    }\n    if (payload.empty) {\n        throw new EmptyResultError(command, payload.reason || `${command} was not found`);\n    }\n    if (!payload.ok || !payload.row) {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dribbble/utils.js#L58-L94","documentation":"requireRows throws this CommandExecutionError when the payload's ok flag is false, meaning the page loaded but the expected DOM structure was not found — the CSS selectors the scraper relies on no longer match. The optional payload.reason from the browser side is appended for detail.","triggerScenarios":"Dribbble redesigned their markup so the command's selectors miss; a bot-check/consent interstitial replaced the results; the page rendered an error or captcha state the scraper interprets as a failed run.","commonSituations":"Site layout changes after being away from updates, running an outdated version of the library against the live site, network conditions producing a partial page render, or hitting Dribbble from a datacenter IP that triggers an anti-bot wall.","solutions":["Update the library to the latest version — selector fixes are typically shipped after site redesigns.","Retry later or from a different network/IP if an anti-bot or consent page is the cause.","Report the issue with payload.reason if updating does not fix it, so the selectors can be repaired.","As a workaround, inspect the page manually to confirm whether the content still exists at all."],"exampleFix":"// before\nconst rows = cli.rows('shots', payload); // 'shots selector drift'\n\n// after\ntry {\n  rows = cli.rows('shots', payload);\n} catch (e) {\n  if (String(e.message).includes('selector drift')) {\n    await upgradeLibrary(); // or notify maintainers with payload.reason\n    payload = await rerunBrowserCommand('shots');\n    rows = cli.rows('shots', payload);\n  } else throw e;\n}","handlingStrategy":"fallback","validationCode":"if (payload && payload.ok === false) {\n  console.warn('Scrape reported failure before requireRows:', payload.reason || 'no reason given');\n}","typeGuard":"function isSuccessfulPayload(p) {\n  return p != null && typeof p === 'object' && p.ok === true;\n}","tryCatchPattern":"try {\n  rows = requireRows(payload, 'shots');\n} catch (e) {\n  if (e instanceof CommandExecutionError && /selector drift/.test(e.message)) {\n    logForMaintainers('shots', payload && payload.reason); // site markup likely changed\n    rows = []; // or fall back to a cached/alternate data source\n  } else throw e;\n}","preventionTips":["Pin and regularly update the library; selector fixes follow Dribbble redesigns.","Watch for anti-bot/consent interstitials and avoid hammering the site from datacenter IPs.","Alert on 'selector drift' messages in production so outages are noticed immediately."],"tags":["selector-drift","scraping","site-change","dom-structure"],"backgroundTag":"selector-drift","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}