{"record":{"id":"7bb070f2bbd9409d","repo":"jackwener/OpenCLI","slug":"trip-com-car-dom-extraction-returned-malformed-row","errorCode":null,"errorMessage":"Trip.com car DOM extraction returned malformed rows","messagePattern":"Trip\\.com car DOM extraction returned malformed rows","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/car.js","lineNumber":59,"sourceCode":"    func: async (page, kwargs) => {\n        const cityId = parseCityId('city', kwargs.city);\n        const limit = parseListLimit(kwargs.limit);\n\n        const listUrl = buildCarListUrl(cityId);\n        await page.goto(listUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_CARS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('trip.com', 'Trip.com is asking for a verification; complete it in your browser session and retry');\n        }\n        if (waitResult === 'empty') {\n            throw new EmptyResultError('trip car', `No car rentals for city id ${cityId}`);\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Trip.com car listing did not render (state=${String(waitResult)}); check the carhire city id`);\n        }\n        const raw = await page.evaluate(buildCarExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Trip.com car DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            throw new CommandExecutionError('Trip.com car cards rendered but parser did not find required price anchors');\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            category: r.category,\n            vehicle: r.vehicle,\n            seats: r.seats,\n            price: r.price,\n            currency: r.currency,\n            url: listUrl,\n        }));\n    },\n});\n","sourceCodeStart":41,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/car.js#L41-L75","documentation":"This CommandExecutionError is thrown when page.evaluate(buildCarExtractJs()) returns a non-array — the car-listing page rendered, but the DOM extraction script produced malformed output instead of a list of car rows. It indicates the extraction script no longer matches the live page structure; empty car lists are reported by error 3907 instead.","triggerScenarios":"Running `trip car` when buildCarExtractJs' in-page script returns null/undefined/an object rather than an array — typically because the carhire card markup changed so the script's row collection fails, or a partially rendered page lacks the expected containers.","commonSituations":"Trip.com redesign of carhire result cards breaking the extract script's selectors; a localized or A/B page variant with different markup; an interstitial or error page that passes the wait check but has no standard car card DOM.","solutions":["Retry once — transient render races can yield malformed extraction; persistent failure indicates a DOM change.","Open the carhire URL in a normal browser and compare card markup against buildCarExtractJs selectors in clis/trip/utils.js.","Update the extraction script's price-anchor selectors to the new Trip.com carhire card structure.","Check for a library update or patch addressing the Trip.com front-end change."],"exampleFix":"// before: assuming array always returned\nconst raw = await page.evaluate(buildCarExtractJs());\nconst rows = raw.slice(0, limit);\n// after: caller-side guard\nconst raw = await page.evaluate(buildCarExtractJs());\nif (!Array.isArray(raw)) throw new Error('Trip.com car extraction non-array; selectors likely stale');\nconst rows = raw.slice(0, limit);","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"function isCarRows(value) {\n  return Array.isArray(value) && value.every(\n    (r) => r && typeof r === 'object' && typeof r.name === 'string'\n  );\n}","tryCatchPattern":"try {\n  return await runCli(['trip', 'car', '--city-id', cityId]);\n} catch (e) {\n  if (/car DOM extraction returned malformed rows/.test(e.message)) {\n    // extraction script out of sync with live carhire DOM\n    throw new Error('Trip.com car extraction stale; update buildCarExtractJs selectors');\n  }\n  throw e;\n}","preventionTips":["Always Array.isArray-check evaluate results before consuming them.","Monitor Trip.com carhire card markup; update price-anchor selectors when it changes.","Treat persistent malformed extraction as a selector-drift bug, not a transient error.","Pin and regression-test extraction selectors against a saved carhire page fixture."],"tags":["dom-parsing","selector-drift","scraping","trip-com"],"backgroundTag":"dom-structure-changed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}