{"record":{"id":"fc72f1ddcc2dc377","repo":"jackwener/OpenCLI","slug":"trip-com-car-cards-rendered-but-parser-did-not-fin","errorCode":null,"errorMessage":"Trip.com car cards rendered but parser did not find required price anchors","messagePattern":"Trip\\.com car cards rendered but parser did not find required price anchors","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/car.js","lineNumber":62,"sourceCode":"\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":44,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/car.js#L44-L75","documentation":"The Trip.com car-hire command's in-page extractor ran successfully but returned an empty array: car cards were present on the page, yet none of them contained the price anchors the parser requires. The CLI treats a rendered-but-unparseable grid as markup drift rather than an empty result, so it throws CommandExecutionError instead of returning [].","triggerScenarios":"Running the Trip.com car search when the page renders car cards but the extractor's price-selector (the required price anchor inside each card) matches nothing, so buildCarExtractJs() filters every row out and returns [].","commonSituations":"Trip.com A/B-testing new card markup; a site redesign changing price element classes/data attributes; regional layouts that omit the price anchor; stale cached HTML; bot-interstitial pages that render card skeletons without prices.","solutions":["Retry the search after a short delay to rule out a partially rendered or cached page","Update the Trip.com session/cookies (complete any verification in the browser session) and retry","Inspect buildCarExtractJs()'s price-anchor selectors against current Trip.com car card markup and update them","Check for a newer version of the CLI package that already tracks the markup change"],"exampleFix":"// before\nif (raw.length === 0) {\n    throw new CommandExecutionError('Trip.com car cards rendered but parser did not find required price anchors');\n}\n// after\nif (raw.length === 0) {\n    throw new CommandExecutionError('Trip.com car cards rendered but parser did not find required price anchors');\n}\n// Long-term: in buildCarExtractJs, add a fallback price selector\n// const price = card.querySelector('.price-text') ?? card.querySelector('[data-price]');","handlingStrategy":"retry","validationCode":"// Pre-check rendered car cards before expecting parseable rows\nconst cardCount = await page.evaluate(() => document.querySelectorAll('[class*=car-card], [data-car-id]').length);\nif (cardCount > 0) console.warn(`${cardCount} car cards rendered; verify price anchors still exist in markup`);","typeGuard":"function isExtractRows(v) {\n    return Array.isArray(v) && v.every(r => r && typeof r === 'object' && 'category' in r);\n}","tryCatchPattern":"try {\n    const cars = await runTripCarSearch(opts);\n} catch (e) {\n    if (e instanceof CommandExecutionError && /price anchors/.test(e.message)) {\n        // markup drift: retry once, then surface a 'site markup changed' alert\n    } else { throw e; }\n}","preventionTips":["Pin test snapshots of Trip.com car card markup and lint extractor selectors against them in CI","Retry transient empty parses once before alerting","Keep the extractor selectors centralized so markup changes are one-line fixes"],"tags":["scraping","dom-parsing","markup-drift","trip-com"],"backgroundTag":"selector-markup-drift","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}