jackwener/OpenCLI · error · CommandExecutionError
Ctrip ferry rows rendered but parser did not find required s
Error message
Ctrip ferry rows rendered but parser did not find required sailing anchors
What it means
CommandExecutionError thrown when the ferry page rendered sailing rows (.list-item-parent cards exist — renderedCardCount > 0) but buildFerryExtractJs returned an empty array because its required sailing anchors (specific links/fields inside each row) matched nothing. The CLI deliberately distinguishes this from EmptyResultError: data is visibly on the page, so an empty parse means the extractor's selectors no longer match Ctrip's row markup. This is a parser/site-contract drift signal, not a data absence.
Source
Thrown at clis/ctrip/ferry.js:70
const limit = parseListLimit(kwargs.limit);
const searchUrl = buildFerryListUrl(fromCity, toCity, date);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_FERRY_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('ship.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Ctrip ferry page did not render sailing rows (state=${String(waitResult)})`);
}
const renderedCardCount = await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));
const raw = await page.evaluate(buildFerryExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Ctrip ferry DOM extraction returned malformed rows');
}
if (raw.length === 0) {
if (Number(renderedCardCount) > 0) {
throw new CommandExecutionError('Ctrip ferry rows rendered but parser did not find required sailing anchors');
}
throw new EmptyResultError('ctrip ferry', `No sailings for ${fromCity} to ${toCity} on ${date}`);
}
return raw.slice(0, limit).map((r, i) => ({
rank: i + 1,
shipName: r.shipName,
departureTime: r.departureTime,
fromPort: r.fromPort,
arrivalTime: r.arrivalTime,
toPort: r.toPort,
duration: r.duration,
price: r.price,
status: r.status,
url: searchUrl,
}));
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect a rendered .list-item-parent row in browser DevTools and update the anchor selectors in buildFerryExtractJs (clis/ctrip/utils.js) to the current markup
- Upgrade opencli in case a newer release already tracks the new ship.ctrip.com markup
- Add a short settle delay or re-scroll before extraction so lazily-attached anchors exist when the extractor runs
- Verify the page region/variant matches what the extractor expects (mainland CN page vs other locale variants)
Example fix
// before (utils.js): extract immediately after scroll
const raw = await page.evaluate(buildFerryExtractJs());
// after: settle, then extract (also update row anchors in DevTools)
await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));
await sleep(500);
const raw = await page.evaluate(buildFerryExtractJs()); Defensive patterns
Strategy: validation
Validate before calling
// Validate that rendered rows expose the anchors the parser needs, before extracting
const anchorsOk = await page.evaluate(() =>
document.querySelectorAll('.list-item-parent a').length > 0
);
if (!anchorsOk) console.error('Ferry row markup changed; update buildFerryExtractJs selectors'); Type guard
function isParserMissError(e) {
return e instanceof Error && e.name === 'CommandExecutionError' && /sailing anchors/.test(e.message);
} Try / catch
try {
return await run(['ctrip', 'ferry', '--from', f, '--to', t, '--date', d]);
} catch (e) {
if (isParserMissError(e)) {
console.error('Rows rendered but selectors missed — inspect .list-item-parent in DevTools and update buildFerryExtractJs');
throw e;
}
throw e;
} Prevention
- Add a weekly smoke test of the ferry command to catch selector drift early
- Allow a settle delay after scrolling so row anchors finish hydrating before extraction
- Keep opencli updated so extractor selectors track ship.ctrip.com redesigns
- Distinguish this error from EmptyResultError in scripts — this one is a parser bug, not missing data
When it happens
Trigger: Calling `ctrip ferry <from> <to> --date <d>` where scrolling confirmed non-zero .list-item-parent cards but the extraction script's required per-row anchor selectors matched zero elements — class renames, restructured row internals, or anchors attaching after extraction ran (late lazy hydration).
Common situations: Ctrip shipping a front-end redesign or A/B test of the sailing row; CLI version predating a markup change; regional page variants with different DOM; scroll-until resolving before inner anchors mount, then extracting too early.
Related errors
- Ctrip cruise cards rendered but parser did not find required
- Ctrip ferry DOM extraction returned malformed rows
- Ctrip round-trip flight cards rendered but parser did not fi
- Ctrip cruise DOM extraction returned malformed rows
- Ctrip ferry page did not render sailing rows (state=${String
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7a8903891c1dae7c.
Report an issue: GitHub.