jackwener/OpenCLI · error · CommandExecutionError

Ctrip place page did not render attraction links for city id

Error message

Ctrip place page did not render attraction links for city id ${cityId} (state=${String(waitResult)}); check the city id

What it means

After the attraction page loads, the wait script must return 'content' (attraction links rendered). Any other state ('captcha' handled separately) means the place page never rendered the `/sight/<city><cityId>/...` links, so the command throws this CommandExecutionError and points at the city id, since by design a valid city always lists attractions.

Source

Thrown at clis/ctrip/attraction.js:56

    ],
    columns: [
        'rank',
        'name',
        'rating', 'reviews',
        'url',
    ],
    func: async (page, kwargs) => {
        const cityId = parseCityId(kwargs.city);
        const limit = parseListLimit(kwargs.limit);

        const placeUrl = buildAttractionPlaceUrl(cityId);
        await page.goto(placeUrl);
        const waitResult = await page.evaluate(buildWaitForAttractionsJs(cityId));
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('you.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip place page did not render attraction links for city id ${cityId} (state=${String(waitResult)}); check the city id`);
        }
        const raw = await page.evaluate(buildAttractionExtractJs(cityId));
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip attraction DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip attraction links rendered but parser did not find required sight anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            name: r.name,
            rating: r.rating,
            reviews: r.reviews,
            url: r.url,
        }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-derive the correct numeric city id with `opencli ctrip search <city>` (e.g. 1 for 北京) and retry with that id.
  2. Verify the id corresponds to a real you.ctrip place page by opening buildAttractionPlaceUrl's URL in a browser.
  3. Retry later or on a better network if the page simply timed out before rendering.
  4. If many valid ids fail, the site layout likely changed — update the CLI adapter.

Example fix

// before
await opencli.ctrip.attraction('beijing'); // non-numeric/invalid id
// after
const city = await opencli.ctrip.search('北京');
await opencli.ctrip.attraction(city[0].id);
Defensive patterns

Strategy: validation

Validate before calling

// only pass numeric city ids obtained from `ctrip search`
const results = await opencli.ctrip.search(cityName);
if (!results.length) throw new Error(`no ctrip city found for ${cityName}`);
const cityId = results[0].id;
if (!/^\d+$/.test(String(cityId))) throw new Error(`unexpected city id: ${cityId}`);

Type guard

const isValidCityId = (id) => typeof id === 'number' ? Number.isInteger(id) && id > 0 : /^\d+$/.test(String(id));

Try / catch

try {
  const rows = await opencli.ctrip.attraction(cityId);
} catch (err) {
  if (err.message.includes('check the city id')) {
    // re-resolve the id via ctrip search before retrying
  }
  throw err;
}

Prevention

When it happens

Trigger: `opencli ctrip attraction <cityId>` with a stale, invalid, or unrenderable city id — the page loads but the DOM never contains city-scoped attraction links within the wait window.

Common situations: Hand-typed city id instead of one from `ctrip search`; a city whose you.ctrip page layout changed; slow page load exceeding the wait timeout; page redirected to an error/landing page.

Related errors


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