jackwener/OpenCLI · error · CommandExecutionError
Trip.com flight DOM extraction returned malformed rows${reas
Error message
Trip.com flight DOM extraction returned malformed rows${reason} What it means
CommandExecutionError thrown when buildFlightExtractJs() returns a non-array value: the flight-card extractor failed rather than producing a row list. When the non-array value carries an in-page error matching /^malformed flight card \d+: [a-z /]+$/, that per-card reason is appended to the message, pinpointing which card and field failed to parse.
Source
Thrown at clis/trip/flight-round.js:73
}
const limit = parseListLimit(kwargs.limit);
const searchUrl = buildFlightRoundSearchUrl(fromCode, toCode, depart, ret);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_FLIGHTS_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('trip.com', 'Trip.com is asking for a verification; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Trip.com flight page did not render flight cards (state=${String(waitResult)})`);
}
const raw = await page.evaluate(buildFlightExtractJs());
if (!Array.isArray(raw)) {
const reason = raw && typeof raw === 'object' && typeof raw.error === 'string'
&& /^malformed flight card \d+: [a-z /]+$/.test(raw.error)
? `: ${raw.error}`
: '';
throw new CommandExecutionError(`Trip.com flight DOM extraction returned malformed rows${reason}`);
}
if (raw.length === 0) {
throw new EmptyResultError('trip flight-round', `No round-trip flights for ${fromCode} to ${toCode} on ${depart} .. ${ret}`);
}
return raw.slice(0, limit).map((r, i) => ({
rank: i + 1,
airline: r.airline,
departureTime: r.departureTime,
departureAirport: r.departureAirport,
arrivalTime: r.arrivalTime,
arrivalAirport: r.arrivalAirport,
duration: r.duration,
stops: r.stops,
price: r.price,
currency: r.currency,
url: searchUrl,
}));
},View on GitHub (pinned to 49907e53dc)
Solutions
- Read the appended reason (e.g. 'malformed flight card 3: missing price') to identify the failing card/field and rerun — partial rendering often resolves on retry
- Loosen or add fallback selectors in buildFlightExtractJs for the field reported missing
- Filter sponsored/promo cards before extraction so variant markup is skipped
- Update the CLI package if the extractor has been fixed upstream
Example fix
// before
const reason = raw && typeof raw === 'object' && typeof raw.error === 'string'
&& /^malformed flight card \d+: [a-z /]+$/.test(raw.error) ? `: ${raw.error}` : '';
throw new CommandExecutionError(`Trip.com flight DOM extraction returned malformed rows${reason}`);
// after
// In buildFlightExtractJs, skip cards missing optional fields instead of failing:
// const price = card.querySelector('.price')?.textContent?.trim();
// if (!price) return null; // filter nulls in the caller
const rows = raw.filter(Boolean); Defensive patterns
Strategy: try-catch
Validate before calling
// Confirm results actually rendered and look complete before extraction
const cardCount = await page.evaluate(() =>
document.querySelectorAll('[class*=flight-card], [data-flight-id]').length);
if (cardCount > 0) console.log(`${cardCount} flight cards rendered; extractor should parse all or fail with a per-card reason`); Type guard
function isFlightRows(v) {
return Array.isArray(v) && v.every(r => r && typeof r === 'object' && 'rank' in r);
}
function hasCardError(v): v is { error: string } {
return !!v && typeof v === 'object' && typeof (v as any).error === 'string';
} Try / catch
try {
const flights = await runFlightRound(args);
} catch (e) {
if (e instanceof CommandExecutionError && /malformed rows/.test(e.message)) {
// log e.message (includes per-card reason) and retry once; persistent failures mean markup drift
} else { throw e; }
} Prevention
- Make the extractor skip (return null for) cards missing optional fields instead of aborting
- Filter sponsored/variant cards before field extraction
- Log the appended per-card reason and monitor it to catch markup changes early
When it happens
Trigger: page.evaluate(buildFlightExtractJs()) resolves to something other than an array — e.g. {error: 'malformed flight card 3: missing price'} — after flights rendered but a card lacked an expected field; empty results take a different path (EmptyResultError) and never reach this throw.
Common situations: Partially rendered results where some cards lack price/duration nodes; Trip.com mixing sponsored cards with different markup into the results list; extractor field regexes too strict for a new card variant; mid-navigation aborting evaluation.
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
- Trip.com car cards rendered but parser did not find required
- Trip.com deals hub rendered but no promotion tiles parsed (t
- Trip.com attraction DOM extraction returned malformed rows
- Trip.com car DOM extraction returned malformed rows
- Trip.com deals page did not render deal tiles (state=${Strin
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4adab75997c5e760.
Report an issue: GitHub.