{"record":{"id":"ed469676f13a9fd4","repo":"jackwener/OpenCLI","slug":"taxonomy-selector-drift-site-powerchina-command","errorCode":null,"errorMessage":"[taxonomy=selector_drift] site=powerchina command=search login required or human verification","messagePattern":"\\[taxonomy=selector_drift\\] site=powerchina command=search login required or human verification","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/powerchina/search.js","lineNumber":224,"sourceCode":"    }\n\n    const rows = filterNavigationRows(\n      dedupeCandidates(extractedRows).map((item) => ({\n        title: cleanText(item.title),\n        url: cleanText(item.url),\n        date: normalizeDate(cleanText(item.date)),\n        contextText: cleanText(item.contextText),\n      })),\n    );\n\n    if (rows.length === 0 && extractedRows.length > 0) {\n      throw new EmptyResultError('powerchina search', 'extracted only navigation/portal rows, no bid entries matched');\n    }\n\n    if (rows.length === 0) {\n      const pageText = cleanText(await page.evaluate('document.body ? document.body.innerText : \"\"'));\n      if (/(请先登录|未登录|登录后|验证码|人机验证)/.test(pageText)) {\n        throw new AuthRequiredError(\n          'bid.powerchina.cn',\n          '[taxonomy=selector_drift] site=powerchina command=search login required or human verification',\n        );\n      }\n      if (apiFailure) {\n        throw new EmptyResultError('powerchina search', `api/dom yielded no result: ${apiFailure}`);\n      }\n    }\n\n    return toProcurementSearchRecords(rows, {\n      site: 'powerchina',\n      query,\n      limit,\n    });\n  },\n});\n\nexport const __test__ = {","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/powerchina/search.js#L206-L242","documentation":"An AuthRequiredError (tagged [taxonomy=selector_drift]) thrown when the powerchina search command finds zero result rows and the rendered page text contains login/captcha markers (请先登录, 未登录, 登录后, 验证码, 人机验证). The library treats 'not logged in' or 'human verification' as the reason the results were missing and surfaces it as an auth-required condition on bid.powerchina.cn, telling the caller credentials/verification are needed rather than that the query failed.","triggerScenarios":"Running powerchina search where rows.length === 0 and page.evaluate('document.body.innerText') matches /(请先登录|未登录|登录后|验证码|人机验证)/ — i.e. the site rendered a login prompt or captcha page instead of results.","commonSituations":"Session cookie expired in the headless browser profile; site rate-limiting the relay IP into a captcha; accessing from a region/IP that forces login; scraping too aggressively triggering anti-bot human verification.","solutions":["Log in to bid.powerchina.cn (persist cookies/session in the browser profile used by the relay) and retry.","Slow down request frequency / add delays to avoid triggering anti-bot verification.","Use a different relay IP/region if the current one is captcha-gated.","Solve the captcha manually once in the persistent browser profile so the session is re-established."],"exampleFix":"// before\nawait cli search powerchina \"招标\" // fails: 未登录 page\n// after\nawait cli login powerchina // refresh session cookies in the relay profile\nawait cli search powerchina \"招标\"","handlingStrategy":"try-catch","validationCode":"// preflight: check for a persisted logged-in session before scraping\nconst pageText = await page.evaluate('document.body ? document.body.innerText : \"\"');\nif (/(请先登录|未登录|登录后|验证码|人机验证)/.test(pageText)) {\n  throw new Error('bid.powerchina.cn session expired or captcha-gated — re-authenticate first');\n}","typeGuard":"function isAuthRequiredError(err) {\n  return err instanceof AuthRequiredError || /login required or human verification/.test(String(err?.message));\n}","tryCatchPattern":"try {\n  rows = await searchPowerchina(query);\n} catch (err) {\n  if (isAuthRequiredError(err)) {\n    await reloginPowerchina();      // refresh cookies in the persistent profile\n    rows = await searchPowerchina(query);\n  } else throw err;\n}","preventionTips":["Persist login cookies for bid.powerchina.cn in the browser profile the relay uses.","Refresh the session proactively before long scrape runs, not just after failures.","Throttle request rate and add jitter to avoid triggering captcha/human verification.","Avoid datacenter IPs known to be captcha-gated; use residential or warmed relay exits."],"tags":["auth","captcha","login-required","anti-bot","scraping"],"backgroundTag":"auth-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}