jackwener/OpenCLI · info · EmptyResultError

No cruises currently departing "${port}"

Error message

No cruises currently departing "${port}"

What it means

EmptyResultError thrown when the port's cruise.ctrip.com results page renders successfully but the WAIT_FOR_CRUISE_JS probe reports state 'empty' — the page explicitly indicates no cruise itineraries exist for that departure port. This is a semantic empty result, distinct from a rendering failure or captcha, and signals the requested port is valid but currently has no listed departures.

Source

Thrown at clis/ctrip/cruise.js:71

        }
        if (indexWait !== 'content') {
            throw new CommandExecutionError(`Ctrip cruise page did not render (state=${String(indexWait)})`);
        }
        const portCode = await page.evaluate(buildCruisePortLookupJs(port));
        if (!portCode) {
            throw new EmptyResultError('ctrip cruise', `No cruise departure port matching "${port}" (try a listed sea-cruise port like 上海 / 威尼斯 / 罗马)`);
        }

        let searchUrl = indexUrl;
        if (portCode !== PORT_INDEX_CODE) {
            searchUrl = buildCruiseSearchUrl(portCode);
            await page.goto(searchUrl);
            const portWait = await page.evaluate(WAIT_FOR_CRUISE_JS);
            if (portWait === 'captcha') {
                throw new AuthRequiredError('cruise.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
            }
            if (portWait === 'empty') {
                throw new EmptyResultError('ctrip cruise', `No cruises currently departing "${port}"`);
            }
            if (portWait !== 'content') {
                throw new CommandExecutionError(`Ctrip cruise port page did not render (state=${String(portWait)})`);
            }
        }

        const raw = await page.evaluate(buildCruiseExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip cruise DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip cruise cards rendered but parser did not find required itinerary anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            star: r.star,
            boarding: r.boarding,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry with a major listed sea-cruise port such as 上海 / 威尼斯 / 罗马 to confirm the command works
  2. Check the port page in a browser to confirm Ctrip truly lists no departures for it
  3. Pick a nearby larger port as the departure point instead
  4. If the port should have sailings, verify the resolved port name is spelled exactly as Ctrip lists it

Example fix

// before: query may hit an empty seasonal port
await run(['ctrip', 'cruise', '三亚']);
// after: fall back to a major hub port
try {
  await run(['ctrip', 'cruise', '三亚']);
} catch (e) {
  if (e.name === 'EmptyResultError') {
    await run(['ctrip', 'cruise', '上海']);
  } else throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-validate the port against Ctrip's listed index before searching departures
const portCode = await page.goto(indexUrl).then(() => page.evaluate(buildCruisePortLookupJs(port)));
if (!portCode) throw new Error(`Unknown port: ${port}`);

Type guard

function isEmptyResultError(e) {
  return e instanceof Error && e.name === 'EmptyResultError';
}

Try / catch

try {
  return await run(['ctrip', 'cruise', port]);
} catch (e) {
  if (isEmptyResultError(e)) {
    return fallbackPorts.map(p => run(['ctrip', 'cruise', p])); // e.g. 上海, 威尼斯, 罗马
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `ctrip cruise <port>` where the resolved port's sN results page loads and renders its 'no products' state, i.e. no `.route_info` cards would ever appear. Note it is only checked on the non-index branch (portCode !== '2'), so a genuinely empty Shanghai port page would instead surface as the parser-missing-anchors error.

Common situations: Querying a small/inland port that seasonally (or permanently) has no cruise departures; searching for a port name that resolves to a real but inactive port code; schedules temporarily withdrawn during off-season or after schedule changes.

Related errors


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