jackwener/OpenCLI · warning · AuthRequiredError

hotels.ctrip.com

Error message

hotels.ctrip.com

What it means

An AuthRequiredError raised when the hotels.ctrip.com list page signals a captcha gate: the WAIT_FOR_SSR_JS probe detected the page path containing 'captcha' or anti-bot text (验证码, verify the human) instead of SSR hotel data in window.__NEXT_DATA__. The domain string 'hotels.ctrip.com' is the error's first argument and becomes the message. It means risk control intercepted the visit and a human session must clear the challenge.

Source

Thrown at clis/ctrip/hotel-search.js:101

    columns: [
        'rank', 'hotelId', 'name', 'enName',
        'star', 'score', 'scoreLabel', 'reviewCount',
        'cityName', 'district', 'address',
        'lat', 'lon',
        'price', 'currency', 'url',
    ],
    func: async (page, kwargs) => {
        const cityId = parseCityId(kwargs.city);
        const checkin = parseIsoDate('checkin', kwargs.checkin);
        const checkout = parseIsoDate('checkout', kwargs.checkout);
        assertCheckinBeforeCheckout(checkin, checkout);
        const limit = parseHotelLimit(kwargs.limit);

        const url = `https://hotels.ctrip.com/hotels/list?city=${cityId}&checkin=${checkin}&checkout=${checkout}`;
        await page.goto(url);
        const waitResult = await page.evaluate(WAIT_FOR_SSR_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('hotels.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip hotel-search page did not expose SSR hotel list (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(EXTRACT_HOTELS_JS);
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip hotel-search returned malformed SSR hotel list');
        }
        if (raw.length === 0) {
            throw new EmptyResultError('ctrip hotel-search', `No hotels for city=${cityId} on ${checkin} → ${checkout}`);
        }
        const rows = raw
            .map((entry, i) => mapHotelRow(entry, i))
            .filter((row) => row.hotelId && row.name)
            .slice(0, limit);
        if (rows.length === 0) {
            throw new CommandExecutionError('Ctrip hotel-search SSR rows were missing required hotelId/name anchors');
        }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Complete the captcha in your linked human browser session, then rerun the command.
  2. Slow down: add delays between city searches and avoid large sequential batches.
  3. Reuse a logged-in Ctrip browser profile so traffic carries trusted cookies.
  4. Switch to a residential IP if datacenter IPs are consistently flagged.

Example fix

// before: 50 cities back-to-back from an anonymous profile
for (const city of cities) await hotelSearch({ city, checkin, checkout });

// after: warm session + throttle
await browserLogin('ctrip');
for (const city of cities) {
  await hotelSearch({ city, checkin, checkout });
  await sleep(3000 + Math.random() * 4000);
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const rows = await ctripHotelSearch({ city, checkin, checkout });
} catch (e) {
  if (e instanceof AuthRequiredError && e.message.includes('hotels.ctrip.com')) {
    await notifyHuman('Ctrip captcha on hotels.ctrip.com — complete it in the browser session');
    return retryWithBackoff(() => ctripHotelSearch({ city, checkin, checkout }), { max: 2 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Navigating to https://hotels.ctrip.com/hotels/list?city=...&checkin=...&checkout=... when Ctrip redirects to /captcha or overlays a verification prompt — typically from flagged IPs, rapid repeated queries, or anonymous browser profiles without trusted cookies.

Common situations: Bulk city queries in a loop from a datacenter IP; CI environments with no persistent cookies; shared proxies previously abused; new/unwarmed browser profiles; Ctrip tightening risk control during peak travel periods.

Related errors


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