jackwener/OpenCLI · info · EmptyResultError
ctrip flight
Error message
ctrip flight
What it means
An EmptyResultError raised when the batchSearch capture succeeded but payload.data.flightItineraryList yielded zero unique itineraries for the requested route/date. The command reports 'ctrip flight' as the empty source plus a human-readable summary like 'No flights for BJS→SHA on 2026-09-01'. This is a legitimate empty result, not a failure of capture or parsing.
Source
Thrown at clis/ctrip/flight.js:199
typeof page?.readNetworkCapture !== 'function' ||
!await page.startNetworkCapture(CAPTURE_PATTERN)) {
throw new CommandExecutionError('Ctrip flight requires browser response interception');
}
await page.readNetworkCapture();
await page.goto(searchUrl);
// The initial document can finish before the large batchSearch body.
// The first rendered card is only a readiness signal; row data still
// comes exclusively from the structured response below.
const readiness = await page.evaluate(WAIT_FOR_BATCH_CAPTURE_JS);
if (readiness === 'captcha') {
throw new AuthRequiredError('flights.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
const itineraries = parseBatchSearchCaptures(await page.readNetworkCapture());
if (!itineraries) {
throw new TimeoutError('Ctrip flight API capture', CAPTURE_TIMEOUT_SECONDS, 'No batchSearch response was observed after opening the results page.');
}
if (itineraries.length === 0) {
throw new EmptyResultError('ctrip flight', `No flights for ${fromCode}→${toCode} on ${date}`);
}
const rows = itineraries
.map((itinerary, index) => mapItinerary(itinerary, searchUrl, index))
// The page groups direct flights before transfers, then applies its
// displayed starting price and departure-time order inside each group.
.sort(([rowA, connectingA, departureA, idA], [rowB, connectingB, departureB, idB]) =>
Number(connectingA) - Number(connectingB) || rowA.price - rowB.price ||
departureA.localeCompare(departureB) || idA.localeCompare(idB))
.slice(0, limit)
.map(([row], index) => ({
rank: index + 1,
airline: row.airline,
flightNo: row.flightNo,
aircraft: row.aircraft,
departureTime: row.departureTime,
departureAirport: row.departureAirport,
arrivalTime: row.arrivalTime,
arrivalAirport: row.arrivalAirport,View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the route actually has flights (search on flights.ctrip.com manually for the same pair/date).
- Pick a nearer date within Ctrip's sellable window.
- Double-check both IATA codes — city codes (BJS) vs airport codes (PEK) behave differently; try the other form.
- Handle EmptyResultError in your caller if empty route/date combinations are expected in your workflow.
Example fix
// before: crash on empty result
const rows = await ctripFlight({ from, to, date });
// after: treat empty as a valid outcome
let rows;
try { rows = await ctripFlight({ from, to, date }); }
catch (e) { if (e instanceof EmptyResultError) rows = []; else throw e; } Defensive patterns
Strategy: try-catch
Validate before calling
// Skip obviously impossible searches before calling
function isPlausibleQuery(from, to, date) {
const d = new Date(date + 'T00:00:00Z');
const now = new Date();
return from !== to && d > now && d < new Date(now.getTime() + 330 * 86400000);
} Try / catch
let rows = [];
try {
rows = await ctripFlight({ from, to, date });
} catch (e) {
if (e instanceof EmptyResultError) {
console.warn(`No flights: ${e.message}`); // treat as valid empty
} else throw e;
} Prevention
- Search dates within Ctrip's bookable window (not in the past, not ~1yr+ ahead).
- Verify route pairs have service on flights.ctrip.com before automating.
- Distinguish city codes (BJS) from airport codes (PEK) when constructing routes.
- Handle EmptyResultError as data, not failure, in batch workflows.
When it happens
Trigger: Running `ctrip flight <from> <to> --date <date>` for a route/date with no bookable inventory: no airline serves the pair, the date is beyond Ctrip's booking horizon (usually ~1 year, often much shorter for some routes), all flights sold out, or the from/to codes resolve to airports with no connections.
Common situations: Searching dates far in the future (schedules not yet loaded); obscure regional airport pairs; past dates; mistyping an IATA code so the pair is nonsensical yet valid per parseIataCode; seasonal routes searched out of season.
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 trains found from ${fromStation.name} to ${toStation.name
- 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
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/965426802e49989d.
Report an issue: GitHub.