{"record":{"id":"ebdf9ebc4b5ed832","repo":"jackwener/OpenCLI","slug":"ctrip-is-asking-for-a-captcha-complete-it-in-your-ebdf9e","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/train.js","lineNumber":57,"sourceCode":"        'departureTime', 'departureStation',\n        'arrivalTime', 'arrivalStation',\n        'duration', 'fromPrice', 'seats',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const fromName = parsePlaceName('from', kwargs.from);\n        const toName = parsePlaceName('to', kwargs.to);\n        if (fromName === toName) {\n            throw new ArgumentError(`--from and --to must differ (got ${fromName})`);\n        }\n        const date = parseIsoDate('date', kwargs.date);\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl = buildTrainListUrl(fromName, toName, date);\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_TRAINS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('trains.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 train page did not render train cards (state=${String(waitResult)})`);\n        }\n        const renderedCardCount = await page.evaluate(buildScrollUntilJs('.card-white.list-item', limit));\n        const raw = await page.evaluate(buildTrainExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip train DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            if (Number(renderedCardCount) > 0) {\n                throw new CommandExecutionError('Ctrip train cards rendered but parser did not find required train anchors');\n            }\n            throw new EmptyResultError('ctrip train', `No trains for ${fromName} to ${toName} on ${date}`);\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            trainNo: r.trainNo,","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/train.js#L39-L75","documentation":"Thrown as AuthRequiredError when the Ctrip train page presents a captcha instead of train results (the in-page wait script reports state 'captcha'). The library drives a persistent browser session and cannot solve captchas itself, so it defers to the user's interactive browser session. It signals that the session's cookies/fingerprint are no longer trusted by trains.ctrip.com.","triggerScenarios":"page.goto(buildTrainListUrl(...)) followed by page.evaluate(WAIT_FOR_TRAINS_JS) returning 'captcha', typically after repeated automated requests or a stale/unauthenticated shared browser profile.","commonSituations":"Scraping many queries in quick succession; running from a datacenter IP; shared browser profile whose Ctrust cookies expired; Ctrip risk controls flagging headless/automated browsing.","solutions":["Open the same browser session/user-data-dir interactively, solve the captcha on trains.ctrip.com, then retry the command.","Reduce request rate and add delays between train queries.","Use a residential IP or different network egress.","Refresh the session by logging into Ctrip in the shared browser profile before retrying."],"exampleFix":"// before (rapid loop)\nfor (const d of dates) await runTrainList({ from, to, date: d });\n// after (throttled)\nfor (const d of dates) { await runTrainList({ from, to, date: d }); await sleep(15000); }","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { await runTrainList(opts); } catch (e) { if (e instanceof AuthRequiredError) { await promptCaptchaSolve(e.domain); return retry(); } throw e; }","preventionTips":["Throttle requests to trains.ctrip.com","Keep the shared browser profile logged in with fresh cookies","Avoid datacenter IPs; prefer residential egress","Solve captches promptly in the interactive session"],"tags":["captcha","auth-required","scraping","anti-bot"],"backgroundTag":"captcha-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}