jackwener/OpenCLI · error · CommandExecutionError
Trip.com hotel DOM extraction returned malformed rows
Error message
Trip.com hotel DOM extraction returned malformed rows
What it means
This CommandExecutionError from clis/trip/hotel-search.js is thrown when buildHotelExtractJs() returns a non-array after the page did reach the content state, meaning the extractor encountered hotel cards whose markup it could not parse. Unlike the flight command, no per-card reason is extracted here — the message is fixed. It almost always reflects a Trip.com DOM/layout change or unusual card content (e.g. ads) rather than user error.
Source
Thrown at clis/trip/hotel-search.js:62
const checkin = parseIsoDate('checkin', kwargs.checkin);
const checkout = parseIsoDate('checkout', kwargs.checkout);
if (checkin >= checkout) {
throw new ArgumentError(`--checkin must be before --checkout (got ${checkin} .. ${checkout})`);
}
const limit = parseListLimit(kwargs.limit);
const searchUrl = buildHotelSearchUrl(cityId, checkin, checkout);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_HOTELS_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 hotel page did not render hotel cards (state=${String(waitResult)})`);
}
const raw = await page.evaluate(buildHotelExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Trip.com hotel DOM extraction returned malformed rows');
}
if (raw.length === 0) {
throw new EmptyResultError('trip hotel-search', `No hotels for city ${cityId} on ${checkin} .. ${checkout}`);
}
return raw.slice(0, limit).map((r, i) => ({
rank: i + 1,
name: r.name,
score: r.score,
reviewLabel: r.reviewLabel,
reviews: r.reviews,
location: r.location,
room: r.room,
price: r.price,
currency: r.currency,
url: searchUrl,
}));
},
});View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the current Trip.com hotel card DOM in a browser and diff against the selectors in buildHotelExtractJs().
- Update buildHotelExtractJs() to handle the new markup and to skip (rather than fail on) non-standard cards.
- Add a debug dump of the non-array return value at throw time to aid future diagnosis.
- Pin/verify against a known-good Trip.com layout and re-run after fixing the extractor.
Example fix
// before
const raw = await page.evaluate(buildHotelExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Trip.com hotel DOM extraction returned malformed rows');
}
// after
const raw = await page.evaluate(buildHotelExtractJs());
if (!Array.isArray(raw)) {
console.error('hotel extract returned:', JSON.stringify(raw).slice(0, 500));
throw new CommandExecutionError(`Trip.com hotel DOM extraction returned malformed rows: ${raw && raw.error || 'unknown'}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
// Post-hoc guard on any scraped payload you consume downstream:
const safeRows = Array.isArray(rows) && rows.every((r) => r && typeof r.hotelName === 'string' && typeof r.price !== 'undefined');
if (!safeRows) console.warn('hotel payload shape unexpected; Trip.com DOM may have changed'); Type guard
const isExtractFailure = (e) => e instanceof CommandExecutionError && /malformed rows/.test(e.message); const isHotelRowArray = (v) => Array.isArray(v) && v.every((r) => r && typeof r === 'object');
Try / catch
try {
const hotels = await runTripHotelSearch(args);
} catch (e) {
if (isExtractFailure(e)) {
console.error('Trip.com hotel extractor needs updating (DOM change likely)');
return null; // degrade and alert
}
throw e;
} Prevention
- Keep buildHotelExtractJs() tolerant of ad/sponsored tiles (skip, don't fail).
- Run periodic smoke hotel searches to catch layout drift early.
- Dump the non-array extractor output when this error fires to speed diagnosis.
- Validate downstream row shapes before relying on results.
When it happens
Trigger: page.evaluate(buildHotelExtractJs()) returns null/undefined/an object instead of an array, immediately after WAIT_FOR_HOTELS_JS returned 'content'.
Common situations: Trip.com rolling out a new hotel card layout or A/B test; sponsored/featured hotel tiles with different DOM interleaved in results; price/availability widgets replacing standard card nodes; regional markup differences.
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 attraction DOM extraction returned malformed rows
- Trip.com car DOM extraction returned malformed rows
- Trip.com car cards rendered but parser did not find required
- Trip.com deals page did not render deal tiles (state=${Strin
- Trip.com deals DOM extraction returned malformed rows
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/347e9de9b284f29b.
Report an issue: GitHub.