jackwener/OpenCLI · error · AuthRequiredError

Ctrip is asking for a captcha; complete it in your browser s

Error message

Ctrip is asking for a captcha; complete it in your browser session and retry

What it means

The ctrip attraction command navigates to the you.ctrip.com place page and runs a wait script via page.evaluate; if the page state resolves to 'captcha', it throws AuthRequiredError because Ctrip is presenting an anti-bot challenge instead of rendering content. The library requires an authenticated browser session (Strategy.COOKIE) and asks you to solve the captcha in that session before retrying.

Source

Thrown at clis/ctrip/attraction.js:53

    args: [
        { name: 'city', required: true, positional: true, help: 'Numeric Ctrip city id (from `ctrip search`, e.g. 1 for 北京)' },
        { name: 'limit', default: 20, help: 'Number of attractions (1-50)' },
    ],
    columns: [
        'rank',
        'name',
        'rating', 'reviews',
        'url',
    ],
    func: async (page, kwargs) => {
        const cityId = parseCityId(kwargs.city);
        const limit = parseListLimit(kwargs.limit);

        const placeUrl = buildAttractionPlaceUrl(cityId);
        await page.goto(placeUrl);
        const waitResult = await page.evaluate(buildWaitForAttractionsJs(cityId));
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('you.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip place page did not render attraction links for city id ${cityId} (state=${String(waitResult)}); check the city id`);
        }
        const raw = await page.evaluate(buildAttractionExtractJs(cityId));
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip attraction DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip attraction links rendered but parser did not find required sight anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            name: r.name,
            rating: r.rating,
            reviews: r.reviews,
            url: r.url,
        }));

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the browser session used by the CLI (the same cookie profile), visit you.ctrip.com manually, and complete the captcha.
  2. Log in via the site's auth command (`ctrip login`) so a fresh, trusted login_uid cookie exists, then retry.
  3. Slow down: add delays between attraction queries and avoid bulk scraping.
  4. Retry from a residential/different IP if you are on a datacenter address.
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure a logged-in session exists before scraping commands
const who = await opencli.ctrip.whoami().catch(() => null);
if (!who) await opencli.ctrip.login();

Try / catch

try {
  const rows = await opencli.ctrip.attraction(cityId);
} catch (err) {
  if (err.name === 'AuthRequiredError' && /captcha/i.test(err.message)) {
    // prompt the user to open the browser session and solve the captcha, then retry
  }
  throw err;
}

Prevention

When it happens

Trigger: Running `opencli ctrip attraction <cityId>` when the headless/cookie-based browser session triggers Ctrip's bot detection: suspicious traffic patterns, missing/expired cookies, datacenter IP, or too-frequent scraping.

Common situations: Running many attraction queries in a row; using a VPN/cloud server IP; session cookies expiring so Ctrip no longer trusts the client; aggressive polling from automation.

Related errors


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