jackwener/OpenCLI · warning · AuthRequiredError

触发了验证码,请先在浏览器中完成滑块验证

Error message

触发了验证码,请先在浏览器中完成滑块验证

What it means

assertNotBlocked scans the page title and first 2000 chars of body text for captcha phrases (请拖动下方滑块完成验证, 验证码, 安全验证, etc.). When one appears — even if the URL doesn't contain /captcha — it throws AuthRequiredError asking the user to complete the slider/captcha verification in the browser. This catches inline captcha overlays embedded in otherwise-normal pages.

Solutions

  1. Complete the slider captcha manually in the browser session
  2. Reduce request rate and add randomized delays between commands
  3. Log in with a valid lianjia_token to lower risk-control sensitivity
  4. Retry from a residential IP / different network
Defensive patterns

Strategy: retry

Validate before calling

const state = await readPageState(page);
const captchaWords = ['验证码','安全验证','滑动验证','请按住滑块','请拖动下方滑块完成验证','访问验证'];
if (captchaWords.some(w => state.title.includes(w) || state.body_text.includes(w))) {
  console.log('Slider captcha detected — complete it in the browser');
}

Try / catch

try {
  await gotoKe(page, url);
} catch (e) {
  if (e instanceof AuthRequiredError && e.message.includes('滑块')) {
    console.error('Complete the slider verification in the browser window, then re-run.');
    process.exitCode = 2;
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: gotoKe lands on a page whose DOM contains a slider-captcha widget or security-verification text (risk control interstitial); Beike injects a滑块 challenge after suspicious request patterns.

Common situations: Aggressive scraping frequency; shared IP previously flagged; new session with no browsing history hitting sensitive endpoints (e.g. house detail pages).

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/42307959a72fd8c0. Report an issue: GitHub.

Appendix: source

Thrown at clis/ke/utils.js:57

    })()
  `);
    if (!result) {
        return { href: '', title: '', body_text: '' };
    }
    return {
        href: cleanText(result.href),
        title: cleanText(result.title),
        body_text: cleanText(result.body_text),
    };
}

export function assertNotBlocked(state) {
    const { href, title, body_text } = state;
    if (href.includes('hip.ke.com/captcha') || href.includes('/captcha')) {
        throw new AuthRequiredError('ke.com', '触发了验证码,请先在浏览器中完成验证');
    }
    if (CAPTCHA_TEXT_PATTERNS.some(p => title.includes(p) || body_text.includes(p))) {
        throw new AuthRequiredError('ke.com', '触发了验证码,请先在浏览器中完成滑块验证');
    }
    if (LOGIN_TEXT_PATTERNS.some(p => title.includes(p))) {
        throw new AuthRequiredError('ke.com', '未登录,请先在浏览器中登录贝壳找房');
    }
}

export async function gotoKe(page, url) {
    await page.goto(url, { settleMs: 2500 });
    await page.wait(2);
    const state = await readPageState(page);
    assertNotBlocked(state);
    return state;
}

/**
 * Fetch a ke.com JSON API from inside the browser context (credentials included).
 */
export async function fetchKeJson(page, url) {

View on GitHub (pinned to 49907e53dc)