jackwener/OpenCLI · info · EmptyResultError

ctrip hotel

Error message

ctrip hotel

What it means

EmptyResultError thrown when the extracted detail object exists but lacks hotelId or name — the minimum fields that constitute a usable hotel detail. The library treats a detail without identity fields as 'no detail exposed' for the given hotel id rather than returning partial data.

Source

Thrown at clis/ctrip/hotel.js:52

        'url',
    ],
    func: async (page, kwargs) => {
        const hotelId = parseHotelId(kwargs.id);
        const url = buildHotelDetailUrl(hotelId);
        await page.goto(url);
        const waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('hotels.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip hotel detail page did not expose SSR hotel data (state=${String(waitResult)})`);
        }
        const detail = await page.evaluate(buildHotelDetailExtractJs());
        if (!detail || typeof detail !== 'object') {
            throw new CommandExecutionError('Ctrip hotel detail SSR extraction returned malformed data');
        }
        if (!detail.hotelId || !detail.name) {
            throw new EmptyResultError('ctrip hotel', `No detail exposed for hotel id ${hotelId}`);
        }
        return [{ ...detail, url }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the hotel URL in a browser to confirm the hotel page still exposes name/id
  2. Check the hotel id is current (re-run hotel-search to get fresh ids)
  3. Update mapHotelDetail/extract script if Ctrip renamed the fields
  4. Update the CLI to the latest version

Example fix

// before
if (!detail.hotelId || !detail.name) throw new EmptyResultError('ctrip hotel', `No detail exposed for hotel id ${hotelId}`);
// after
if (!detail.hotelId) detail.hotelId = String(hotelId); // fall back to the requested id
if (!detail.hotelId || !detail.name) throw new EmptyResultError('ctrip hotel', `No detail exposed for hotel id ${hotelId}`);
Defensive patterns

Strategy: type-guard

Validate before calling

// after extraction, before relying on the detail
if (typeof detail?.name !== 'string' || !detail.name) console.warn(`hotel ${hotelId} exposes no name — possibly delisted`);

Type guard

const isUsableDetail = (d) => !!d && typeof d === 'object' && !!d.hotelId && typeof d.name === 'string' && d.name.length > 0;

Try / catch

try {
  return await ctrip.hotel({ id });
} catch (e) {
  if (e instanceof EmptyResultError) {
    return null; // hotel has no exposed detail — treat as not found
  }
  throw e;
}

Prevention

When it happens

Trigger: buildHotelDetailExtractJs() returns an object whose hotelId/name keys are absent or empty: the page rendered but the identity block (title/hotel id) uses new markup, or the hotel is delisted and renders a shell page.

Common situations: Querying a closed/renamed hotel whose page still resolves; Ctrip renaming identity fields in its SSR payload; overseas/regional variants omitting these fields; stale CLI after a Ctrip update.

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/be92d6153fa2e94b. Report an issue: GitHub.