{"record":{"id":"7e88ac0534b772cc","repo":"jackwener/OpenCLI","slug":"no-trains-for-fromname-to-toname-on-date","errorCode":null,"errorMessage":"No trains for ${fromName} to ${toName} on ${date}","messagePattern":"No trains for (.+?) to (.+?) on (.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/ctrip/train.js","lineNumber":71,"sourceCode":"        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,\n            arrivalStation: r.arrivalStation,\n            duration: r.duration,\n            fromPrice: r.fromPrice,\n            seats: Array.isArray(r.seats) && r.seats.length ? r.seats.join(' / ') : null,\n            url: searchUrl,\n        }));\n    },\n});\n","sourceCodeStart":53,"sourceCodeEnd":87,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/train.js#L53-L87","documentation":"Thrown as EmptyResultError when the Ctrip train page rendered fine but zero train rows were extracted and no cards were rendered — i.e. Ctrip genuinely has no trains for the requested route/date. This is a normal empty result, not a failure of the scraper.","triggerScenarios":"raw.length === 0 with Number(renderedCardCount) === 0 for the given fromName/toName/date, e.g. an unbookable or future-date-not-on-sale route, or a route with no direct trains.","commonSituations":"Querying a date beyond Ctrip's sales window (typically ~15 days); minor station pairs with no service; holidays where schedules changed; misspelled station resolving to a real but unserved place.","solutions":["Confirm the route actually has train service and try a nearby major station pair.","Query a date within the booking window (usually the next ~15 days).","Handle EmptyResultError as an expected outcome in your script and continue.","Verify the resolved fromName/toName are the intended stations."],"exampleFix":"// before\nconst trains = await runTrainList({ from, to, date });\nconsole.log(trains[0]);\n// after\ntry { const trains = await runTrainList({ from, to, date }); if (!trains.length) console.log('no trains'); }\ncatch (e) { if (e instanceof EmptyResultError) console.log('no trains for route/date'); else throw e; }","handlingStrategy":"fallback","validationCode":"if (new Date(opts.date) > salesWindowEnd()) console.warn('date likely beyond booking window');","typeGuard":"const isBookableDate = (d) => { const t = new Date(d).getTime(); return t >= Date.now() && t <= Date.now() + 15*864e5; };","tryCatchPattern":"try { return await runTrainList(opts); } catch (e) { if (e instanceof EmptyResultError) return []; throw e; }","preventionTips":["Query dates within Ctrip's booking window (~15 days)","Treat empty results as expected for unserved routes","Verify station names resolve to served stations","Alternate nearby major stations when a pair returns nothing"],"tags":["empty-result","scraping","no-data"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}