jackwener/OpenCLI · error · AuthRequiredError

vacations.ctrip.com

Error message

vacations.ctrip.com

What it means

AuthRequiredError thrown when the Ctrip vacations (flight+hotel package) page presents a captcha. WAIT_FOR_VACATIONS_JS returns 'captcha' and the library requires the user to solve it in their browser session before package search can proceed. Automatic retries will keep failing until the captcha is cleared.

Source

Thrown at clis/ctrip/package.js:47

        { name: 'destination', required: true, positional: true, help: 'Destination keyword (e.g. 三亚 / 北京 / 曼谷)' },
        { name: 'limit', default: 20, help: 'Number of packages (1-50)' },
    ],
    columns: [
        'rank',
        'title', 'subtitle',
        'tags', 'score', 'sold', 'reviews',
        'price',
        'url',
    ],
    func: async (page, kwargs) => {
        const destination = parsePlaceName('destination', kwargs.destination);
        const limit = parseListLimit(kwargs.limit);

        const searchUrl = buildPackageListUrl(destination);
        await page.goto(searchUrl);
        const waitResult = await page.evaluate(WAIT_FOR_VACATIONS_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('vacations.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult === 'empty') {
            throw new EmptyResultError('ctrip package', `No flight-plus-hotel packages for "${destination}"`);
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip package page did not render package cards (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(buildVacationsExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip package DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip package cards rendered but parser did not find required package anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            subtitle: r.subtitle,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Solve the captcha in the interactive browser session, then re-run the command
  2. Re-authenticate/refresh the CLI's browser profile cookies
  3. Throttle request frequency and add jittered delays
  4. Use a residential IP or rotate network exit

Example fix

// before
await ctrip.package({ destination: '三亚' }); // throws on captcha
// after
try {
  return await ctrip.package({ destination: '三亚' });
} catch (e) {
  if (e instanceof AuthRequiredError) {
    await promptUserToSolveCaptchaInBrowser();
    return await ctrip.package({ destination: '三亚' });
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

// cannot be validated pre-call; detect captcha state after load
const state = await page.evaluate(WAIT_FOR_VACATIONS_JS);
if (state === 'captcha') await requireManualCaptchaResolution();

Type guard

const isAuthRequiredError = (e) => e instanceof AuthRequiredError || e?.name === 'AuthRequiredError';

Try / catch

try {
  return await ctrip.package({ destination });
} catch (e) {
  if (isAuthRequiredError(e)) {
    await promptUserToSolveCaptcha();
    return await ctrip.package({ destination }); // retry once after manual solve
  }
  throw e;
}

Prevention

When it happens

Trigger: page.evaluate(WAIT_FOR_VACATIONS_JS) resolves 'captcha' after page.goto(buildPackageListUrl(destination)) — anti-bot detection triggered by automation, blocked IP, or stale session cookies on vacations.ctrip.com.

Common situations: Rapid repeated package searches from a server IP; first-time access from a new region/IP; expired connect session; headless browsing fingerprint detected.

Related errors


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