{"record":{"id":"eb1c5adf85716c64","repo":"jackwener/OpenCLI","slug":"hotels-ctrip-com-eb1c5a","errorCode":null,"errorMessage":"hotels.ctrip.com","messagePattern":"hotels\\.ctrip\\.com","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/hotel.js","lineNumber":42,"sourceCode":"    browser: true,\n    navigateBefore: false,\n    args: [\n        { name: 'id', required: true, positional: true, help: 'Numeric Ctrip hotel id (use `ctrip hotel-suggest` to discover; e.g. 375539)' },\n    ],\n    columns: [\n        'hotelId', 'name', 'enName',\n        'star', 'score', 'scoreLabel', 'reviewCount', 'ratingBreakdown',\n        'facilities', 'checkInOut',\n        'cityName', 'address', 'lat', 'lon',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const hotelId = parseHotelId(kwargs.id);\n        const url = buildHotelDetailUrl(hotelId);\n        await page.goto(url);\n        const waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('hotels.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 hotel detail page did not expose SSR hotel data (state=${String(waitResult)})`);\n        }\n        const detail = await page.evaluate(buildHotelDetailExtractJs());\n        if (!detail || typeof detail !== 'object') {\n            throw new CommandExecutionError('Ctrip hotel detail SSR extraction returned malformed data');\n        }\n        if (!detail.hotelId || !detail.name) {\n            throw new EmptyResultError('ctrip hotel', `No detail exposed for hotel id ${hotelId}`);\n        }\n        return [{ ...detail, url }];\n    },\n});\n","sourceCodeStart":24,"sourceCodeEnd":57,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/hotel.js#L24-L57","documentation":"AuthRequiredError thrown when the Ctrip hotel detail page presents a captcha instead of hotel content. WAIT_FOR_HOTEL_DETAIL_JS returns 'captcha' and the library signals that the user must complete the captcha in a real browser session before automated access can continue. It is not retryable automatically.","triggerScenarios":"page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS) resolves with 'captcha' after page.goto(buildHotelDetailUrl(hotelId)) — Ctrip's anti-bot layer detected automation, the session cookies are stale/blocked, or the IP is rate-limited.","commonSituations":"Scraping too fast from a datacenter IP; expired connect-browser session cookies; running headless without a fingerprint; repeated detail-page requests in a loop.","solutions":["Open the same browser session, solve the captcha, then retry the command","Refresh/re-login the persistent browser profile the CLI uses","Slow down request rate and add delays between detail fetches","Switch to a residential IP or different network"],"exampleFix":"// before\nawait ctrip.hotel({ id: hotelId }); // throws AuthRequiredError when captcha'd\n// after\ntry {\n  return await ctrip.hotel({ id: hotelId });\n} catch (e) {\n  if (e instanceof AuthRequiredError) {\n    await openBrowserForManualCaptcha(); // user completes captcha in session\n    return await ctrip.hotel({ id: hotelId });\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"// no pre-call validation possible; detect captcha state after load instead\nconst state = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);\nif (state === 'captcha') await requireManualCaptchaResolution();","typeGuard":"const isAuthRequiredError = (e) => e instanceof AuthRequiredError || e?.name === 'AuthRequiredError';","tryCatchPattern":"try {\n  return await ctrip.hotel({ id });\n} catch (e) {\n  if (isAuthRequiredError(e)) {\n    await promptUserToSolveCaptcha(); // interactive browser session\n    return await ctrip.hotel({ id }); // single retry after manual solve\n  }\n  throw e;\n}","preventionTips":["Keep the browser session cookies fresh","Throttle detail-page requests and add jittered delays","Avoid datacenter IPs; prefer residential networks","Pause scraping when consecutive captcha errors occur"],"tags":["captcha","anti-bot","auth","scraping"],"backgroundTag":"captcha-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}