jackwener/OpenCLI · error · CommandExecutionError

Ctrip cruise cards rendered but parser did not find required

Error message

Ctrip cruise cards rendered but parser did not find required itinerary anchors

What it means

CommandExecutionError thrown when the extraction script returns a well-formed but EMPTY array even though the page rendered (wait state was 'content'). The CLI distinguishes this from EmptyResultError: cards are visibly on the page, but buildCruiseExtractJs could not find the required itinerary anchors (e.g. links/fields inside .route_info cards it needs to build rows). This almost always means Ctrip changed its card markup so the parser's selectors no longer match.

Source

Thrown at clis/ctrip/cruise.js:83

            await page.goto(searchUrl);
            const portWait = await page.evaluate(WAIT_FOR_CRUISE_JS);
            if (portWait === 'captcha') {
                throw new AuthRequiredError('cruise.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
            }
            if (portWait === 'empty') {
                throw new EmptyResultError('ctrip cruise', `No cruises currently departing "${port}"`);
            }
            if (portWait !== 'content') {
                throw new CommandExecutionError(`Ctrip cruise port page did not render (state=${String(portWait)})`);
            }
        }

        const raw = await page.evaluate(buildCruiseExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip cruise DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip cruise cards rendered but parser did not find required itinerary anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            star: r.star,
            boarding: r.boarding,
            sailingDate: r.sailingDate,
            tags: r.tags,
            price: r.price,
            url: searchUrl,
        }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the port results page in a browser DevTools and diff the actual card markup against the selectors in buildCruiseExtractJs (clis/ctrip/utils.js)
  2. Update/fix the extraction selectors in clis/ctrip/utils.js to match the current .route_info structure
  3. Upgrade the opencli package in case a newer version already tracks the new Ctrip markup
  4. Add a short settle delay/scroll before extraction if anchors attach late during lazy hydration

Example fix

// before (utils.js): anchor selector outdated
const link = card.querySelector('a.itinerary-link');
if (!link) return null;
// after: match new markup (verify in DevTools)
const link = card.querySelector('a[href*="/cruise/"], .route_title a');
if (!link) return null;
Defensive patterns

Strategy: validation

Validate before calling

// Validate the required anchors still exist in the live DOM before full extraction
const anchorsOk = await page.evaluate(() =>
  document.querySelectorAll('.route_info a').length > 0
);
if (!anchorsOk) console.error('Ctrip card markup changed; update buildCruiseExtractJs selectors');

Type guard

function isParserMissError(e) {
  return e instanceof Error && e.name === 'CommandExecutionError' && /itinerary anchors/.test(e.message);
}

Try / catch

try {
  return await run(['ctrip', 'cruise', port]);
} catch (e) {
  if (isParserMissError(e)) {
    console.error('Ctrip markup likely changed — inspect .route_info in DevTools and update selectors');
    throw e;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `ctrip cruise <port>` where the page passed the content probe but the extraction script's required itinerary anchor selectors (inside .route_info cards) matched zero elements — markup redesign, renamed classes, or content moved behind lazy-loaded subcomponents after the probe ran.

Common situations: Ctrip front-end redeploy changing .route_info internals (A/B tests, redesigns); CLI version older than a recent site markup change; region-specific page variants (overseas ports) rendered with slightly different DOM; partial lazy-load so cards exist but their anchors haven't attached yet.

Related errors


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