jackwener/OpenCLI · error · CommandExecutionError
Ctrip bus DOM extraction returned malformed rows
Error message
Ctrip bus DOM extraction returned malformed rows
What it means
After the page renders and scrolls, buildBusExtractJs runs in the page to parse schedule rows. If it does not return an array, the extraction is structurally malformed and the CLI throws CommandExecutionError rather than returning garbage. This indicates the page's DOM no longer matches what the extract script expects.
Source
Thrown at clis/ctrip/bus.js:66
if (fromCity === toCity) {
throw new ArgumentError(`--from and --to must differ (got ${fromCity})`);
}
const date = parseIsoDate('date', kwargs.date);
const limit = parseListLimit(kwargs.limit);
const searchUrl = buildBusListUrl(fromCity, toCity, date);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_BUS_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('bus.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Ctrip bus page did not render schedule rows (state=${String(waitResult)})`);
}
const renderedCardCount = await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));
const raw = await page.evaluate(buildBusExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Ctrip bus DOM extraction returned malformed rows');
}
if (raw.length === 0) {
if (Number(renderedCardCount) > 0) {
throw new CommandExecutionError('Ctrip bus rows rendered but parser did not find required schedule anchors');
}
throw new EmptyResultError('ctrip bus', `No coaches for ${fromCity} to ${toCity} on ${date}`);
}
return raw.slice(0, limit).map((r, i) => ({
rank: i + 1,
departureTime: r.departureTime,
fromStation: r.fromStation,
toStation: r.toStation,
duration: r.duration,
price: r.price,
status: r.status,
url: searchUrl,
}));
},View on GitHub (pinned to 49907e53dc)
Solutions
- Rerun the command to rule out a transient A/B variant
- Inspect the current bus.ctrip.com DOM and update the selectors inside buildBusExtractJs
- Pin or update the CLI version so the extract script matches the live Ctrip markup
- Check whether the page hit an error variant by logging the page URL/HTML when this fires
Example fix
// before
const raw = await page.evaluate(buildBusExtractJs());
if (!Array.isArray(raw)) { throw new CommandExecutionError('...malformed rows'); }
// after (user-side mitigation: retry once before failing)
let raw = await page.evaluate(buildBusExtractJs());
if (!Array.isArray(raw)) { await page.reload(); raw = await page.evaluate(buildBusExtractJs()); } Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try {
rows = await ctripBusList({ from, to, date });
} catch (e) {
if (e instanceof CommandExecutionError && e.message.includes('malformed rows')) {
console.error('Ctrip DOM no longer matches extract script — update the CLI or pin a working version');
} else {
throw e;
}
} Prevention
- Pin the CLI version and test against live ctrip markup after ctrip deploys changes
- Detect ctrip A/B redesigns early with a smoke-test run in CI
- Fail fast and report instead of retrying — a markup change will not heal on retry
- Keep buildBusExtractJs selectors in one maintained place
When it happens
Trigger: `clis ctrip bus` where page.evaluate(buildBusExtractJs()) returns undefined/null/an object instead of an array — typically because the Ctrip list-page markup changed or the script ran against an unexpected page variant.
Common situations: Ctrip front-end redesign moving or renaming row containers; A/B test bucket giving a different DOM; evaluate returning a non-serializable wrapper after an injected-script error.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Ctrip attraction DOM extraction returned malformed rows
- Ctrip attraction links rendered but parser did not find requ
- Ctrip bus rows rendered but parser did not find required sch
- Not a git repository
- Working tree not clean: ${status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/8672849e61915b25.
Report an issue: GitHub.