jackwener/OpenCLI · error · CommandExecutionError

Ctrip tour page did not render package cards (state=${String

Error message

Ctrip tour page did not render package cards (state=${String(waitResult)})

What it means

A CommandExecutionError raised by `ctrip tour` when the wait helper returned an unrecognized state (neither 'captcha', 'empty', nor 'content'), meaning the tour search page finished loading without rendering package cards. The state value is embedded in the message for diagnosis. The library cannot distinguish navigation failure, timeout, or markup change, so it fails with the observed state.

Source

Thrown at clis/ctrip/tour.js:53

        'tags', 'score', 'sold', 'reviews',
        'price',
        'url',
    ],
    func: async (page, kwargs) => {
        const destination = parsePlaceName('destination', kwargs.destination);
        const limit = parseListLimit(kwargs.limit);

        const searchUrl = buildTourListUrl(destination);
        await page.goto(searchUrl);
        const waitResult = await page.evaluate(WAIT_FOR_VACATIONS_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('vacations.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult === 'empty') {
            throw new EmptyResultError('ctrip tour', `No tour packages for "${destination}"`);
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip tour page did not render package cards (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(buildVacationsExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip tour DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip tour cards rendered but parser did not find required package anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            subtitle: r.subtitle,
            tags: r.tags,
            score: r.score,
            sold: r.sold,
            reviews: r.reviews,
            price: r.price,
            url: searchUrl,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command — transient render slowness is common
  2. Inspect the state=... value in the message to identify which branch of WAIT_FOR_VACATIONS_JS produced it
  3. Confirm .list_product_item cards appear on the tour search URL in a real browser
  4. Stabilize your network/proxy for the headless browser
  5. Update the CLI so the wait helper matches current Ctrip markup

Example fix

// before
if (waitResult !== 'content') throw new CommandExecutionError(`state=${String(waitResult)}`);
// after
if (waitResult !== 'content') {
  await page.waitForSelector('.list_product_item', { timeout: 30000 }).catch(() => {});
  if (!(await page.$('.list_product_item'))) throw new CommandExecutionError(`state=${String(waitResult)}`);
}
Defensive patterns

Strategy: retry

Validate before calling

const dest = String(process.argv[2] || '').trim();
if (!dest) { console.error('destination required'); process.exit(1); }

Try / catch

try {
  const tours = await runCli('ctrip', 'tour', dest);
} catch (e) {
  if (e.name === 'CommandExecutionError' && /state=/.test(e.message)) {
    await sleep(5000); // retry transient render failure
  } else throw e;
}

Prevention

When it happens

Trigger: Running `opencli ctrip tour <destination>` where page.evaluate(WAIT_FOR_VACATIONS_JS) resolves to an unexpected value — timeouts waiting for .list_product_item, evaluate errors returning undefined/null, or the page redirecting away from the results view.

Common situations: Slow network leaving results unrendered before the helper's timeout; Ctrip A/B or redesign removing polled selectors; headless environments where lazy hydration never completes; intermittent evaluate failures.

Related errors


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