jackwener/OpenCLI · error · CommandExecutionError

Ctrip hotel detail page did not expose SSR hotel data (state

Error message

Ctrip hotel detail page did not expose SSR hotel data (state=${String(waitResult)})

What it means

CommandExecutionError thrown when the detail page loaded but WAIT_FOR_HOTEL_DETAIL_JS returned a state other than 'content' or 'captcha', meaning the expected SSR hotel data never appeared (e.g. 'timeout', 'error', 'redirect'). The message embeds the state for diagnosis.

Source

Thrown at clis/ctrip/hotel.js:45

        { name: 'id', required: true, positional: true, help: 'Numeric Ctrip hotel id (use `ctrip hotel-suggest` to discover; e.g. 375539)' },
    ],
    columns: [
        'hotelId', 'name', 'enName',
        'star', 'score', 'scoreLabel', 'reviewCount', 'ratingBreakdown',
        'facilities', 'checkInOut',
        'cityName', 'address', 'lat', 'lon',
        '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. Verify the hotelId is valid by opening buildHotelDetailUrl(hotelId) in a browser and checking the state value in the message
  2. Increase the wait timeout in WAIT_FOR_HOTEL_DETAIL_JS if on a slow connection
  3. Update the wait/selector logic to the current Ctrip markup
  4. Retry — transient slowness can cause the timeout state

Example fix

// before
const waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);
if (waitResult !== 'content') throw new CommandExecutionError(`...(state=${waitResult})`);
// after
let waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);
if (waitResult !== 'content') {
  await page.waitForTimeout(3000);
  waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);
}
if (waitResult !== 'content') throw new CommandExecutionError(`...(state=${waitResult})`);
Defensive patterns

Strategy: retry

Validate before calling

// validate the id format before the call
if (!/^\d+$/.test(String(hotelId))) throw new Error(`invalid ctrip hotel id: ${hotelId}`);

Type guard

const isContentState = (state) => state === 'content';

Try / catch

try {
  return await ctrip.hotel({ id });
} catch (e) {
  if (e instanceof CommandExecutionError && /did not expose SSR hotel data/.test(e.message)) {
    await sleep(2000);
    return await ctrip.hotel({ id }); // bounded retry for timeout states
  }
  throw e;
}

Prevention

When it happens

Trigger: WAIT_FOR_HOTEL_DETAIL_JS times out waiting for SSR data, the page redirects to a booking/login flow, the hotelId is invalid and Ctrip serves an error page, or the DOM selector for the SSR state never matches.

Common situations: Typo or nonexistent hotelId producing a 404-ish page; Ctrip A/B redesign removing the awaited selector; slow network causing the internal wait timeout; mobile vs desktop page variant.

Related errors


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