jackwener/OpenCLI · error · CommandExecutionError
Booking.com extractor returned malformed hotel row
Error message
Booking.com extractor returned malformed hotel row
What it means
This CommandExecutionError is thrown while mapping individual extractor items when an element is not a usable object (null, undefined, or a non-object). The library validates each hotel row before normalizing its fields (name, country, slug, url), so a single malformed row aborts the whole result mapping rather than silently emitting garbage.
Source
Thrown at clis/booking/search.js:312
const items = raw.items;
if (items.length === 0) {
const totalText = String(raw.totalText || '').trim();
if (hasPositiveResultCount(totalText)) {
throw new CommandExecutionError(
`Booking.com page declared results but no property cards were parsed: ${totalText}`,
);
}
throw new EmptyResultError(
`booking search ${JSON.stringify(destination)}`,
totalText
? `No hotels rendered (${totalText}). Try a broader destination, different dates, or check the URL in a browser.`
: 'No hotels rendered. Try a broader destination, different dates, or check the URL in a browser.',
);
}
return items.slice(0, limit).map((it, i) => {
if (!it || typeof it !== 'object') {
throw new CommandExecutionError('Booking.com extractor returned malformed hotel row');
}
const name = String(it.name || '').trim();
const country = String(it.country || '').trim();
const slug = String(it.slug || '').trim();
const urlValue = String(it.url || '').trim();
const expectedUrl = country && slug
? `https://www.booking.com/hotel/${country}/${slug}.html`
: '';
if (!name || !/^[a-z]{2}$/.test(country) || !slug || urlValue !== expectedUrl) {
throw new CommandExecutionError('Booking.com hotel row is missing stable name/url identity');
}
return {
rank: offset + i + 1,
name,
country,
slug,
star_rating: it.star_rating,
review_score: it.review_score,View on GitHub (pinned to 49907e53dc)
Solutions
- Update the library so the extractor never emits non-object rows.
- Log raw.items to find the offending entry and its index.
- Retry the search — a lazy-load race may produce complete rows on a second attempt.
- If using a custom extractor, filter falsy rows before returning: items.filter(it => it && typeof it === 'object').
Example fix
// before (custom extractor)
return { ok: true, items: cards.map(parseCard) };
// after
return { ok: true, items: cards.map(parseCard).filter(it => it && typeof it === 'object') }; Defensive patterns
Strategy: type-guard
Validate before calling
// pre-filter raw items yourself if you control the extractor output const safeItems = (Array.isArray(rawItems) ? rawItems : []).filter(it => it && typeof it === 'object');
Type guard
const isHotelRow = (it) => it !== null && typeof it === 'object' && typeof (it.name ?? it.url ?? '') !== 'undefined';
Try / catch
try {
return await booking.search(params);
} catch (e) {
if (/malformed hotel row/.test(e.message)) {
await sleep(2000);
return booking.search(params); // lazy-load race often resolves on retry
}
throw e;
} Prevention
- Retry once — partial renders during lazy-load commonly cause bad rows.
- Keep the extractor's row parser returning objects for every card.
- Update the library after Booking.com DOM/A-B test changes.
- Filter falsy entries in items at the extractor level before mapping.
When it happens
Trigger: raw.items is an array but contains a null/undefined entry or a primitive — e.g. the extractor pushed a placeholder/falsy value, or a DOM change made its row-parser return undefined for some cards.
Common situations: Partially rendered page where some cards lacked expected structure and the extractor appended null; a custom/patched extractor with inconsistent row parsing; DOM updates mid-extraction (lazy-load race) producing incomplete rows; site A/B tests changing card markup for a subset of results.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${label} returned an unexpected payload shape; expected an o
- Bilibili conclusion API returned malformed outline section
- Bilibili conclusion API returned malformed outline point
- Booking.com extractor returned malformed items
- douyin hashtag ${action}: API returned malformed "${field}"
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/18963f6f3612e6b7.
Report an issue: GitHub.