{"record":{"id":"9e86748f6c78b38a","repo":"jackwener/OpenCLI","slug":"ctrip-train-page-did-not-render-train-cards-state","errorCode":null,"errorMessage":"Ctrip train page did not render train cards (state=${String(waitResult)})","messagePattern":"Ctrip train page did not render train cards \\(state=(.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/train.js","lineNumber":60,"sourceCode":"        '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,\n            departureTime: r.departureTime,\n            departureStation: r.departureStation,\n            arrivalTime: r.arrivalTime,","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/train.js#L42-L78","documentation":"Thrown as CommandExecutionError when the Ctrip train page loads but never reaches the expected 'content' state: the in-page wait script returned something other than 'content' or 'captcha'. The DOM never rendered train cards, so scraping cannot proceed. This indicates a page-structure or load problem rather than bad input.","triggerScenarios":"WAIT_FOR_TRAINS_JS times out or reports states like 'timeout'/'error' after page.goto(searchUrl); Ctrip markup changed, slow network, or the page served an unexpected interstitial.","commonSituations":"Ctrip A/B-changing their train list DOM; slow proxy or throttled network causing render timeout; regional redirects to a different page layout; JS errors on the page blocking rendering.","solutions":["Retry the query once — transient slowness often causes this.","Check WAIT_FOR_TRAINS_JS selectors against current trains.ctrip.com markup and update if Ctrip changed the page.","Increase the wait timeout if on a slow connection.","Run in a visible (non-headless) browser to see what the page actually shows."],"exampleFix":"// before\nconst raw = await scrapeTrains({ from, to, date });\n// after\nlet raw;\ntry { raw = await scrapeTrains({ from, to, date }); }\ncatch (e) { if (e instanceof CommandExecutionError) raw = await retry(scrapeTrains, { from, to, date }); else throw e; }","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try { return await runTrainList(opts); } catch (e) { if (e instanceof CommandExecutionError && /did not render/.test(e.message)) { await sleep(5000); return retryOnce(opts); } throw e; }","preventionTips":["Retry transient render failures once with backoff","Run on a stable/fast network","Pin/patch WAIT_FOR_TRAINS_JS selectors when Ctrip updates markup","Debug headlessly-invisible pages with headful runs"],"tags":["scraping","dom-render","timeout","page-structure"],"backgroundTag":"page-render-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}