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 cruise command navigates to the Ctrip cruise index and waits via WAIT_FOR_CRUISE_JS. If the returned state is 'captcha', Ctrip's anti-bot protection is intercepting cruise.ctrip.com, and the CLI throws AuthRequiredError telling the user to solve the captcha in their own browser session and retry.
Source
Thrown at clis/ctrip/cruise.js:52
{ name: 'port', required: true, positional: true, help: 'Departure cruise port name (e.g. 上海 / 威尼斯 / 罗马)' },
{ name: 'limit', default: 20, help: 'Number of cruises (1-50)' },
],
columns: [
'rank',
'title', 'star',
'boarding', 'sailingDate',
'tags', 'price',
'url',
],
func: async (page, kwargs) => {
const port = parsePlaceName('port', kwargs.port);
const limit = parseListLimit(kwargs.limit);
const indexUrl = buildCruiseSearchUrl(PORT_INDEX_CODE);
await page.goto(indexUrl);
const indexWait = await page.evaluate(WAIT_FOR_CRUISE_JS);
if (indexWait === 'captcha') {
throw new AuthRequiredError('cruise.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (indexWait !== 'content') {
throw new CommandExecutionError(`Ctrip cruise page did not render (state=${String(indexWait)})`);
}
const portCode = await page.evaluate(buildCruisePortLookupJs(port));
if (!portCode) {
throw new EmptyResultError('ctrip cruise', `No cruise departure port matching "${port}" (try a listed sea-cruise port like 上海 / 威尼斯 / 罗马)`);
}
let searchUrl = indexUrl;
if (portCode !== PORT_INDEX_CODE) {
searchUrl = buildCruiseSearchUrl(portCode);
await page.goto(searchUrl);
const portWait = await page.evaluate(WAIT_FOR_CRUISE_JS);
if (portWait === 'captcha') {
throw new AuthRequiredError('cruise.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (portWait === 'empty') {View on GitHub (pinned to 49907e53dc)
Solutions
- Open the same browser session, complete the captcha on cruise.ctrip.com, then rerun
- Use a persistent browser profile with established Ctrip cookies
- Throttle query frequency and add jitter/delays between runs
- Try a different network/IP if flagged persistently
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
cruises = await ctripCruiseList({ port });
} catch (e) {
if (e instanceof AuthRequiredError && e.message.includes('captcha')) {
await promptManualCaptchaResolution('cruise.ctrip.com');
cruises = await ctripCruiseList({ port });
} else {
throw e;
}
} Prevention
- Reuse a persistent browser session with prior human activity on ctrip
- Throttle cruise queries; avoid rapid successive runs from the same IP
- Alert a human on AuthRequiredError instead of automated retries
- Prefer residential networks over datacenter IPs for ctrip access
When it happens
Trigger: `clis ctrip cruise` where page.evaluate(WAIT_FOR_CRUISE_JS) on buildCruiseSearchUrl(PORT_INDEX_CODE) returns 'captcha' — the session or IP was flagged by Ctrip's WAF.
Common situations: High-frequency cruise queries triggering rate limits; headless/automation-flagged browser; shared datacenter IP with poor reputation; expired session cookies prompting re-verification.
Related errors
- Ctrip is asking for a captcha; complete it in your browser s
- Ctrip is asking for a captcha; complete it in your browser s
- 请先在共享 Chrome 完成 1688 登录/验证,再重试(${action})
- Ctrip is asking for a captcha; complete it in your browser s
- Ctrip is asking for a captcha; complete it in your browser s
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/65f8497c3a06474f.
Report an issue: GitHub.