jackwener/OpenCLI · warning · EmptyResultError
No trains for ${fromName} to ${toName} on ${date}
Error message
No trains for ${fromName} to ${toName} on ${date} What it means
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.
Source
Thrown at clis/ctrip/train.js:71
const searchUrl = buildTrainListUrl(fromName, toName, date);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_TRAINS_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('trains.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Ctrip train page did not render train cards (state=${String(waitResult)})`);
}
const renderedCardCount = await page.evaluate(buildScrollUntilJs('.card-white.list-item', limit));
const raw = await page.evaluate(buildTrainExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Ctrip train DOM extraction returned malformed rows');
}
if (raw.length === 0) {
if (Number(renderedCardCount) > 0) {
throw new CommandExecutionError('Ctrip train cards rendered but parser did not find required train anchors');
}
throw new EmptyResultError('ctrip train', `No trains for ${fromName} to ${toName} on ${date}`);
}
return raw.slice(0, limit).map((r, i) => ({
rank: i + 1,
trainNo: r.trainNo,
departureTime: r.departureTime,
departureStation: r.departureStation,
arrivalTime: r.arrivalTime,
arrivalStation: r.arrivalStation,
duration: r.duration,
fromPrice: r.fromPrice,
seats: Array.isArray(r.seats) && r.seats.length ? r.seats.join(' / ') : null,
url: searchUrl,
}));
},
});
View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before
const trains = await runTrainList({ from, to, date });
console.log(trains[0]);
// after
try { const trains = await runTrainList({ from, to, date }); if (!trains.length) console.log('no trains'); }
catch (e) { if (e instanceof EmptyResultError) console.log('no trains for route/date'); else throw e; } Defensive patterns
Strategy: fallback
Validate before calling
if (new Date(opts.date) > salesWindowEnd()) console.warn('date likely beyond booking window'); Type guard
const isBookableDate = (d) => { const t = new Date(d).getTime(); return t >= Date.now() && t <= Date.now() + 15*864e5; }; Try / catch
try { return await runTrainList(opts); } catch (e) { if (e instanceof EmptyResultError) return []; throw e; } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- No coaches for ${fromCity} to ${toCity} on ${date}
- EMPTY_RESULT
- No attractions for "${query}"
- No timetable for ${from} to ${to} (${country})
- No airport transfers for ${city} (${airport})
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7e88ac0534b772cc.
Report an issue: GitHub.