{"record":{"id":"06c79354aca81cd2","repo":"jackwener/OpenCLI","slug":"ctrip-is-asking-for-a-captcha-complete-it-in-your-06c793","errorCode":null,"errorMessage":"Ctrip is asking for a captcha; complete it in your browser session and retry","messagePattern":"Ctrip is asking for a captcha; complete it in your browser session and retry","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/flight-round.js","lineNumber":84,"sourceCode":"        const fromCode = parseIataCode('from', kwargs.from);\n        const toCode = parseIataCode('to', kwargs.to);\n        if (fromCode === toCode) {\n            throw new ArgumentError(`--from and --to must differ (got ${fromCode})`);\n        }\n        const depart = parseIsoDate('depart', kwargs.depart);\n        const ret = parseIsoDate('return', kwargs.return);\n        if (ret < depart) {\n            throw new ArgumentError(`--return (${ret}) must be on or after --depart (${depart})`);\n        }\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl =\n            `https://flights.ctrip.com/online/list/round-${fromCode.toLowerCase()}-${toCode.toLowerCase()}` +\n            `?depdate=${depart}_${ret}&cabin=Y_S_C_F&adult=1&child=0&infant=0`;\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_FLIGHTS_ROUND_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('flights.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Ctrip round-trip flight page did not render flight cards (state=${String(waitResult)})`);\n        }\n        const renderedCardCount = await page.evaluate(buildScrollUntilJs(ROUND_CARD_SELECTOR, limit));\n        const raw = await page.evaluate(buildFlightExtractJs(ROUND_CARD_SELECTOR, false));\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip round-trip flight DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            if (Number(renderedCardCount) > 0) {\n                throw new CommandExecutionError('Ctrip round-trip flight cards rendered but parser did not find required flight anchors');\n            }\n            throw new EmptyResultError('ctrip flight-round', `No round-trip flights for ${fromCode}→${toCode} on ${depart} / ${ret}`);\n        }\n        const completeRows = raw\n            .filter((r) => r.departureTime && r.departureAirport && r.arrivalTime && r.arrivalAirport && r.airline)\n            .slice(0, limit)","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight-round.js#L66-L102","documentation":"An AuthRequiredError thrown when the wait-for-content script running inside the Ctrip round-trip flight page detects Ctrip's captcha challenge (waitResult === 'captcha'). The CLI drives a logged-in Chrome/Chromium session and cannot solve captchas itself, so it defers to the user: solve the captcha in that browser session and retry the command.","triggerScenarios":"page.goto to the round-trip flight search URL renders a captcha instead of flight cards — Ctrip's anti-bot has flagged the automated browser session and WAIT_FOR_FLIGHTS_ROUND_JS returns 'captcha'.","commonSituations":"Repeated rapid scraping from one IP; running many commands back-to-back in CI; a fresh browser profile with no Ctrip cookies triggering bot detection; Ctrip escalating risk checks during peak booking times.","solutions":["Open the Ctrip page in the same Chrome/Chromium session the CLI uses and complete the captcha manually, then rerun the command.","Log in to flights.ctrip.com in that browser to establish trusted cookies before retrying.","Slow down request cadence, add delays between CLI invocations, or run from a residential IP.","If captchas recur constantly, reduce automation volume or use a different authenticated profile."],"exampleFix":"// before (naive loop that keeps hitting captcha)\nfor (const d of dates) cli(['flight-round', ..., '--depart', d, '--return', addDays(d)]);\n// after\nfor (const d of dates) {\n  try { cli(['flight-round', ...]); } catch (e) {\n    if (e instanceof AuthRequiredError) { promptUserToSolveCaptcha('flights.ctrip.com'); retryOnce(); }\n    else throw e;\n  }\n  await sleep(5000);\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"function isAuthRequiredError(e) { return e && (e.code === 'AUTH_REQUIRED' || e instanceof AuthRequiredError); }","tryCatchPattern":"try { await cli(['flight-round', ...]); }\ncatch (e) {\n  if (isAuthRequiredError(e)) { openBrowserForCaptcha('flights.ctrip.com'); return retryOnce(); }\n  throw e;\n}","preventionTips":["Keep the CLI's Chrome profile logged in to flights.ctrip.com before batch runs.","Throttle commands; avoid tight loops against Ctrip.","Run from a stable residential IP rather than datacenter IPs.","Alert on AUTH_REQUIRED exit code so operators solve captchas promptly."],"tags":["captcha","auth-required","anti-bot","ctrip","browser-automation"],"backgroundTag":"captcha-challenge","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}