jackwener/OpenCLI · error · CommandExecutionError
Ctrip cruise DOM extraction returned malformed rows
Error message
Ctrip cruise DOM extraction returned malformed rows
What it means
CommandExecutionError thrown when buildCruiseExtractJs's page.evaluate returns a non-array value, meaning the in-page extraction script did not return the expected array of cruise rows. This is a parser-contract violation: the extraction JS itself failed or was subverted (e.g. page context returned something unexpected, script serialization failed, or Ctrip injected conflicting globals). It signals the CLI's DOM contract with the page broke at the structural level.
Source
Thrown at clis/ctrip/cruise.js:80
let searchUrl = indexUrl;
if (portCode !== PORT_INDEX_CODE) {
searchUrl = buildCruiseSearchUrl(portCode);
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
- Rerun the command — if it reproduces consistently, the extraction script itself is broken, not the page data
- Verify buildCruiseExtractJs in clis/ctrip/utils.js returns an explicit array (e.g. `return rows;` where rows is an Array) and is not transpiled into an unserializable closure
- Check you are running the CLI unbundled as intended (raw ESM via node), since function serialization to page.evaluate is fragile under bundling
- Log the actual page URL at extraction time to rule out a redirect to a non-results page
Example fix
// before (utils.js): implicit/fragile return
const rows = document.querySelectorAll('.route_info');
return { rows }; // object, not array -> triggers 903
// after
const rows = [...document.querySelectorAll('.route_info')];
return rows.map(r => ({...})); // always an array Defensive patterns
Strategy: type-guard
Validate before calling
// Validate the extraction helper exists and is a plain function before the CLI evaluates it
import { buildCruiseExtractJs } from './utils.js';
if (typeof buildCruiseExtractJs !== 'function') throw new Error('buildCruiseExtractJs missing'); Type guard
function isNonArrayExtraction(v) {
return v === null || v === undefined || !Array.isArray(v);
}
// in wrapper code:
const raw = await page.evaluate(buildCruiseExtractJs());
if (isNonArrayExtraction(raw)) throw new Error('extractor returned non-array'); Try / catch
try {
return await run(['ctrip', 'cruise', port]);
} catch (e) {
if (e instanceof Error && /malformed rows/.test(e.message)) {
console.error('Extraction contract broken: run CLI unbundled and check buildCruiseExtractJs');
throw e;
}
throw e;
} Prevention
- Never bundle/minify the helper modules passed into page.evaluate — serialization breaks closures
- Ensure buildCruiseExtractJs returns an explicit Array on every code path
- Pin the opencli version and test extraction after any upgrade
- Check for mid-command page redirects by logging location.href at extraction time
When it happens
Trigger: Calling `ctrip cruise <port>` where page.evaluate(buildCruiseExtractJs()) resolves to undefined/null/non-array — typically because the extraction script threw in-page and the bridge returned undefined, or the evaluated function was mangled (minifier/transpile issue) so it no longer returns an array.
Common situations: Bundling/transpiling clis/ctrip/utils.js in a way that breaks the serialized function passed to page.evaluate; page CSP or injected scripts interfering with evaluation; version mismatch where the CLI and its helpers were partially upgraded; a page redirect landing on a non-results URL right before extraction.
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 ferry DOM extraction returned malformed rows
- Ctrip cruise cards rendered but parser did not find required
- Ctrip round-trip flight DOM extraction returned malformed ro
- Ctrip round-trip flight cards rendered but parser did not fi
- Ctrip round-trip flight rows were missing required airline/f
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4a8ea377ee4893b3.
Report an issue: GitHub.