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

  1. Verify the route actually has flights (search on flights.ctrip.com manually for the same pair/date).
  2. Pick a nearer date within Ctrip's sellable window.
  3. Double-check both IATA codes — city codes (BJS) vs airport codes (PEK) behave differently; try the other form.
  4. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/965426802e49989d. Report an issue: GitHub.