jackwener/OpenCLI · warning · AuthRequiredError
flights.ctrip.com
Error message
flights.ctrip.com
What it means
An AuthRequiredError raised when the flight results page signals a captcha gate: the in-page WAIT_FOR_BATCH_CAPTURE_JS probe detected location.pathname containing 'captcha' or captcha/human-verification text (验证码, verify the human, 安全验证) in the document body instead of flight cards. The domain ('flights.ctrip.com') is the first argument and is what surfaces as the error message. It tells the agent/operator a human-in-the-loop session is needed before scraping can continue.
Source
Thrown at clis/ctrip/flight.js:192
const date = parseIsoDate('date', kwargs.date);
const limit = parseFlightLimit(kwargs.limit);
const searchUrl =
`https://flights.ctrip.com/online/list/oneway-${fromCode.toLowerCase()}-${toCode.toLowerCase()}` +
`?depdate=${date}&cabin=Y_S_C_F&adult=1&child=0&infant=0`;
if (typeof page?.startNetworkCapture !== 'function' ||
typeof page?.readNetworkCapture !== 'function' ||
!await page.startNetworkCapture(CAPTURE_PATTERN)) {
throw new CommandExecutionError('Ctrip flight requires browser response interception');
}
await page.readNetworkCapture();
await page.goto(searchUrl);
// The initial document can finish before the large batchSearch body.
// The first rendered card is only a readiness signal; row data still
// comes exclusively from the structured response below.
const readiness = await page.evaluate(WAIT_FOR_BATCH_CAPTURE_JS);
if (readiness === 'captcha') {
throw new AuthRequiredError('flights.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
const itineraries = parseBatchSearchCaptures(await page.readNetworkCapture());
if (!itineraries) {
throw new TimeoutError('Ctrip flight API capture', CAPTURE_TIMEOUT_SECONDS, 'No batchSearch response was observed after opening the results page.');
}
if (itineraries.length === 0) {
throw new EmptyResultError('ctrip flight', `No flights for ${fromCode}→${toCode} on ${date}`);
}
const rows = itineraries
.map((itinerary, index) => mapItinerary(itinerary, searchUrl, index))
// The page groups direct flights before transfers, then applies its
// displayed starting price and departure-time order inside each group.
.sort(([rowA, connectingA, departureA, idA], [rowB, connectingB, departureB, idB]) =>
Number(connectingA) - Number(connectingB) || rowA.price - rowB.price ||
departureA.localeCompare(departureB) || idA.localeCompare(idB))
.slice(0, limit)
.map(([row], index) => ({
rank: index + 1,View on GitHub (pinned to 49907e53dc)
Solutions
- Open flights.ctrip.com in the linked human browser session and complete the captcha, then retry the command.
- Reduce query frequency and add jitter/delays between searches.
- Use a residential IP or your normal browser profile (with existing cookies) instead of a fresh automated profile.
- Log in to a Ctrip account in the browser session so risk control trusts the traffic.
Example fix
// before: tight loop from anonymous profile triggers captcha
for (const date of dates) await searchFlights({ from, to, date });
// after: reuse a logged-in session and throttle
await browserLogin('ctrip');
for (const date of dates) {
await searchFlights({ from, to, date });
await sleep(5000 + Math.random() * 5000);
} Defensive patterns
Strategy: retry
Try / catch
try {
const rows = await ctripFlight({ from, to, date });
} catch (e) {
if (e instanceof AuthRequiredError && e.message.includes('flights.ctrip.com')) {
// surface to a human: open the browser session and complete the captcha,
// then retry with backoff
await notifyHuman('Complete the Ctrip captcha, then press enter');
return retryWithBackoff(() => ctripFlight({ from, to, date }), { max: 3 });
}
throw e;
} Prevention
- Keep a logged-in Ctrip session with persistent cookies for automated runs.
- Throttle searches with randomized delays; avoid tight loops from cloud IPs.
- Prefer residential IPs over datacenter ranges.
- Stop and re-authenticate at the first captcha signal instead of hammering retries.
When it happens
Trigger: Opening https://flights.ctrip.com/online/list/oneway-<from>-<to>?depdate=... when Ctrip's risk control redirects the page to /captcha or renders a verification overlay; typically after repeated automated queries, from a datacenter IP, or with a cold/anonymous browser profile lacking trusted cookies.
Common situations: High-frequency polling in CI from cloud IPs; running without the user's logged-in browser session (Strategy relies on session state); shared VPN/proxy IP flagged by Ctrip; sudden risk-control tightening on flights.ctrip.com.
Related errors
- hotels.ctrip.com
- dianping.com
- 请先在共享 Chrome 完成 1688 登录/验证,再重试(${action})
- ${config.site} login
- Browser session required for bilibili follow
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3254c03548706e22.
Report an issue: GitHub.