jackwener/OpenCLI · error · CommandExecutionError

Ctrip hotel-search page did not expose SSR hotel list (state

Error message

Ctrip hotel-search page did not expose SSR hotel list (state=${String(waitResult)})

What it means

A CommandExecutionError raised when the SSR wait probe on the hotels.ctrip.com list page resolved to a state other than 'content' or 'captcha' — in practice 'timeout': within 5 seconds the MutationObserver never saw window.__NEXT_DATA__.props.pageProps.initListData.hotelList become an array. The message includes the observed state so you can tell timeout apart from captcha. The library throws it because without the SSR payload there is nothing to extract.

Source

Thrown at clis/ctrip/hotel-search.js:104

        'cityName', 'district', 'address',
        'lat', 'lon',
        'price', 'currency', 'url',
    ],
    func: async (page, kwargs) => {
        const cityId = parseCityId(kwargs.city);
        const checkin = parseIsoDate('checkin', kwargs.checkin);
        const checkout = parseIsoDate('checkout', kwargs.checkout);
        assertCheckinBeforeCheckout(checkin, checkout);
        const limit = parseHotelLimit(kwargs.limit);

        const url = `https://hotels.ctrip.com/hotels/list?city=${cityId}&checkin=${checkin}&checkout=${checkout}`;
        await page.goto(url);
        const waitResult = await page.evaluate(WAIT_FOR_SSR_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-search page did not expose SSR hotel list (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(EXTRACT_HOTELS_JS);
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip hotel-search returned malformed SSR hotel list');
        }
        if (raw.length === 0) {
            throw new EmptyResultError('ctrip hotel-search', `No hotels for city=${cityId} on ${checkin} → ${checkout}`);
        }
        const rows = raw
            .map((entry, i) => mapHotelRow(entry, i))
            .filter((row) => row.hotelId && row.name)
            .slice(0, limit);
        if (rows.length === 0) {
            throw new CommandExecutionError('Ctrip hotel-search SSR rows were missing required hotelId/name anchors');
        }
        return rows;
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry — transient server slowness is the most common cause of the 'timeout' state.
  2. Check manually what the URL renders in your browser session; if it's an anti-bot wall, complete verification and retry.
  3. If slowness is chronic, raise the 5s timeout in WAIT_FOR_SSR_JS.
  4. If Ctrip changed the page structure, update the __NEXT_DATA__ path (initListData.hotelList) in WAIT_FOR_SSR_JS/EXTRACT_HOTELS_JS.

Example fix

// before
setTimeout(() => { observer.disconnect(); resolve('timeout'); }, 5000);

// after: allow slower page loads
setTimeout(() => { observer.disconnect(); resolve('timeout'); }, 15000);
Defensive patterns

Strategy: retry

Try / catch

try {
  const rows = await ctripHotelSearch({ city, checkin, checkout });
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.includes('did not expose SSR hotel list')) {
    const state = /state=(\w+)/.exec(e.message)?.[1];
    if (state === 'timeout') {
      return retryWithBackoff(() => ctripHotelSearch({ city, checkin, checkout }), { attempts: 2, baseMs: 5000 });
    }
  }
  throw e;
}

Prevention

When it happens

Trigger: The Next.js hydration payload never appears within 5s (slow network/server), Ctrip serves a client-only variant of the page for this UA/region, the page loaded an error/redirect shell, or the page was a soft anti-bot wall that didn't match the captcha text heuristics.

Common situations: Slow links or congested Ctrip servers during peak booking hours; Ctrip A/B-testing a new page layout that relocates initListData; unusual user-agent or locale served a different (non-SSR) template; intermittent soft-block responses lacking captcha markers.

Related errors


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