jackwener/OpenCLI · error · CommandExecutionError

Ctrip train cards rendered but parser did not find required

Error message

Ctrip train cards rendered but parser did not find required train anchors

What it means

Thrown as CommandExecutionError when the page visibly rendered train cards (scroll-until counted renderedCardCount > 0) but the extractor produced zero parseable rows. This means the DOM anchors the extractor requires (per-train identifiers) were not found, i.e. the page layout changed relative to the parser's expectations.

Source

Thrown at clis/ctrip/train.js:69

        const limit = parseListLimit(kwargs.limit);

        const searchUrl = buildTrainListUrl(fromName, toName, date);
        await page.goto(searchUrl);
        const waitResult = await page.evaluate(WAIT_FOR_TRAINS_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('trains.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip train page did not render train cards (state=${String(waitResult)})`);
        }
        const renderedCardCount = await page.evaluate(buildScrollUntilJs('.card-white.list-item', limit));
        const raw = await page.evaluate(buildTrainExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip train DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            if (Number(renderedCardCount) > 0) {
                throw new CommandExecutionError('Ctrip train cards rendered but parser did not find required train anchors');
            }
            throw new EmptyResultError('ctrip train', `No trains for ${fromName} to ${toName} on ${date}`);
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            trainNo: r.trainNo,
            departureTime: r.departureTime,
            departureStation: r.departureStation,
            arrivalTime: r.arrivalTime,
            arrivalStation: r.arrivalStation,
            duration: r.duration,
            fromPrice: r.fromPrice,
            seats: Array.isArray(r.seats) && r.seats.length ? r.seats.join(' / ') : null,
            url: searchUrl,
        }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect a rendered card in DevTools and update the anchors required by buildTrainExtractJs.
  2. Re-run after a full page load / extra settle delay in case anchors render late.
  3. Check for Ctrip A/B variants by re-running in a fresh session.

Example fix

// before (extractor selector)
document.querySelector('.train-num')
// after (updated to current markup)
document.querySelector('[data-train-no], .train-number')
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try { return await runTrainList(opts); } catch (e) { if (e instanceof CommandExecutionError && /train anchors/.test(e.message)) { await sleep(3000); return retryOnce(opts); } throw e; }

Prevention

When it happens

Trigger: buildScrollUntilJs('.card-white.list-item', limit) counted cards, but buildTrainExtractJs() returned an empty array because required train anchors (train number elements etc.) are missing or renamed in the DOM.

Common situations: Ctrip partial redesign: card wrapper class unchanged but inner anchor attributes/classes changed; lazy-rendered fields not present when extraction runs; A/B test variant lacking the expected anchors.

Related errors


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