jackwener/OpenCLI · warning · EmptyResultError
No trains found from ${fromStation.name} to ${toStation.name
Error message
No trains found from ${fromStation.name} to ${toStation.name} on ${date} What it means
EmptyResultError thrown when the 12306 query succeeded but, after decoding and parsing every returned train row, zero valid records remained. This distinguishes a genuinely empty result (no trains for that route/date) from a protocol error, and suggests trying a different date or verifying the route exists.
Source
Thrown at clis/12306/trains.js:145
const date = validateDate(kwargs.date);
const limit = normalizeLimit(kwargs.limit, 50, MAX_LIMIT);
const stations = await fetchStationBundle();
const fromStation = resolveStation(stations, fromArg);
const toStation = resolveStation(stations, toArg);
if (fromStation.code === toStation.code) {
throw new ArgumentError(`<from> and <to> must differ; both resolved to ${fromStation.name} (${fromStation.code})`);
}
const stationByCode = new Map(stations.map((s) => [s.code, s]));
const cookieHeader = await mintSession();
const rawRows = await queryLeftTickets(cookieHeader, fromStation.code, toStation.code, date);
const decoded = rawRows
.map((line) => parseTrainRecord(decodeURIComponent(line.replace(/%0A/g, '')), stationByCode))
.filter(Boolean);
if (decoded.length === 0) {
throw new EmptyResultError(
`No trains found from ${fromStation.name} to ${toStation.name} on ${date}`,
'Try a different date or check whether the route is operated by 12306.',
);
}
return decoded.slice(0, limit);
},
});
export const __test__ = { extractQueryEndpoint, queryLeftTickets };
View on GitHub (pinned to 49907e53dc)
Solutions
- Pick a date within the 12306 pre-sale window (typically 15 days ahead) and in the future.
- Verify the route has direct service — try the major stations of each city (e.g. 北京 to 上海) to confirm the corridor has trains.
- Try adjacent dates; some routes run only on certain weekdays or seasons.
- If you expected trains but get none consistently, check whether parseTrainRecord is silently dropping rows (12306 may have changed the record field count); log rawRows length vs decoded length.
Defensive patterns
Strategy: try-catch
Validate before calling
const days = (Date.parse(date + 'T00:00:00Z') - Date.now()) / 86400000;
if (days < 0 || days > 15) {
console.warn(`date ${date} is outside the 12306 pre-sale window (0-15 days ahead); expect no trains`);
} Type guard
function isWithinPresaleWindow(dateStr) {
const t = Date.parse(dateStr + 'T00:00:00Z');
if (Number.isNaN(t)) return false;
const days = (t - Date.now()) / 86400000;
return days >= 0 && days <= 15;
} Try / catch
try {
rows = await trains({ from, to, date });
} catch (e) {
if (e.name === 'EmptyResultError' && /No trains found/.test(e.message)) {
console.warn(e.message, '-', e.hint || 'try the city main stations or an adjacent date');
return []; // handle empty gracefully instead of crashing
}
throw e;
} Prevention
- Query dates within the pre-sale window (roughly 15 days ahead) and in the future.
- Use major city stations to confirm a corridor has service before narrowing to small stations.
- Try adjacent dates for weekend-only or seasonal routes.
- If consistently empty for a known-served route, suspect a parsing regression and compare rawRows vs decoded counts.
When it happens
Trigger: A query for a route/date where 12306 returns no train rows (or rows that all fail parseTrainRecord's `f.length < 33` check, so filter(Boolean) drops them): a date beyond the 15-day booking window, a route with no direct trains, a past date, or a schedule change leaving the response empty.
Common situations: Booking too far in advance (outside the pre-sale period); querying weekend-only or seasonal routes on non-operating days; querying a past date; typo'd but valid stations with no direct connection (e.g. small stations requiring transfer); 12306 schedule updates removing a service.
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_DATA
- No Wayback snapshots for "${target}".
- Chess.com has no game archives for ${username}
- Chess.com has games archives for ${username} but no games in
- Chess.com returned no stats for ${username}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e6a041ec3d1dc88c.
Report an issue: GitHub.