{"record":{"id":"a2c8ab93f3653e2d","repo":"jackwener/OpenCLI","slug":"booking-com-extractor-returned-malformed-items","errorCode":null,"errorMessage":"Booking.com extractor returned malformed items","messagePattern":"Booking\\.com extractor returned malformed items","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/booking/search.js","lineNumber":291,"sourceCode":"    } catch (err) {\n      throw new CommandExecutionError(`Failed to extract Booking.com cards: ${err?.message || err}`);\n    }\n\n    if (raw && typeof raw === 'object' && raw.data && raw.session) {\n      raw = raw.data;\n    }\n    if (!raw || typeof raw !== 'object') {\n      throw new CommandExecutionError('Booking.com page returned no extractable data');\n    }\n    if (raw.blocked) {\n      throw new CommandExecutionError('Booking.com served a verification / captcha page; retry later or change profile');\n    }\n\n    if (raw.ok !== true) {\n      throw new CommandExecutionError('Booking.com extractor returned an invalid status');\n    }\n    if (!Array.isArray(raw.items)) {\n      throw new CommandExecutionError('Booking.com extractor returned malformed items');\n    }\n\n    const items = raw.items;\n    if (items.length === 0) {\n      const totalText = String(raw.totalText || '').trim();\n      if (hasPositiveResultCount(totalText)) {\n        throw new CommandExecutionError(\n          `Booking.com page declared results but no property cards were parsed: ${totalText}`,\n        );\n      }\n      throw new EmptyResultError(\n        `booking search ${JSON.stringify(destination)}`,\n        totalText\n          ? `No hotels rendered (${totalText}). Try a broader destination, different dates, or check the URL in a browser.`\n          : 'No hotels rendered. Try a broader destination, different dates, or check the URL in a browser.',\n      );\n    }\n","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/booking/search.js#L273-L309","documentation":"This CommandExecutionError is thrown when the extractor reported ok:true but raw.items is not an array. The library expects the extractor to always return its results as an array; any other type means the payload is malformed and processing individual hotel rows cannot proceed safely.","triggerScenarios":"The extractor returned an object with ok:true but items missing, null, or a non-array (e.g. an object map or undefined because its internal querySelectorAll matched nothing and it forgot to default to []).","commonSituations":"Booking.com DOM change broke the card-collection selector so the extractor never built the items array; extractor version mismatch after a library or site update; a custom extractor returning results in a different shape.","solutions":["Update the library — a DOM change usually breaks both items collection and this check together.","Log/inspect raw.items to see its actual type and value.","Retry the search in case the page was in a transitional state during extraction.","If using a custom extractor, ensure items is always an array (default to [] when nothing matches)."],"exampleFix":"// before (custom extractor)\nreturn { ok: true, items: cards.map(parse) };\n// after\nconst cards = [...document.querySelectorAll('[data-testid=property-card]')];\nreturn { ok: true, items: (cards || []).map(parse) };","handlingStrategy":"validation","validationCode":"// sanity-check that a search is expected to return items before calling\nif (!destination || !checkin || !checkout) throw new Error('destination and dates are required for a results-bearing search');","typeGuard":"const hasItemsArray = (raw) => raw != null && typeof raw === 'object' && raw.ok === true && Array.isArray(raw.items);","tryCatchPattern":"try {\n  return await booking.search(params);\n} catch (e) {\n  if (/malformed items/.test(e.message)) {\n    await sleep(2000);\n    return booking.search(params); // transient extraction state\n  }\n  throw e;\n}","preventionTips":["Update the library when Booking.com changes card markup.","Retry once — transitional page states can yield non-array payloads.","If using a custom extractor, always default items to [].","Pin and test extractor behavior against a saved results page."],"tags":["scraping","schema-validation","browser-automation"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}