{"record":{"id":"8817893758121e85","repo":"jackwener/OpenCLI","slug":"ctrip-attraction-dom-extraction-returned-malformed","errorCode":null,"errorMessage":"Ctrip attraction DOM extraction returned malformed rows","messagePattern":"Ctrip attraction DOM extraction returned malformed rows","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/attraction.js","lineNumber":60,"sourceCode":"        'rating', 'reviews',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const cityId = parseCityId(kwargs.city);\n        const limit = parseListLimit(kwargs.limit);\n\n        const placeUrl = buildAttractionPlaceUrl(cityId);\n        await page.goto(placeUrl);\n        const waitResult = await page.evaluate(buildWaitForAttractionsJs(cityId));\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('you.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Ctrip place page did not render attraction links for city id ${cityId} (state=${String(waitResult)}); check the city id`);\n        }\n        const raw = await page.evaluate(buildAttractionExtractJs(cityId));\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip attraction DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            throw new CommandExecutionError('Ctrip attraction links rendered but parser did not find required sight anchors');\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            name: r.name,\n            rating: r.rating,\n            reviews: r.reviews,\n            url: r.url,\n        }));\n    },\n});\n","sourceCodeStart":42,"sourceCodeEnd":74,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/attraction.js#L42-L74","documentation":"buildAttractionExtractJs is expected to return an array of parsed attraction rows from the page DOM; if page.evaluate returns anything other than an array (null, undefined, object), the command throws this CommandExecutionError. It indicates the extraction script and the actual page structure are out of sync rather than a data problem.","triggerScenarios":"Running the ctrip attraction command when the extraction script's return value isn't an array — e.g. the page markup changed so the script bails and returns null/undefined, or an exception inside the evaluate was swallowed.","commonSituations":"Ctrip updated the you.ctrip.com DOM structure; adapter version older than a recent site change; unusual regional variant of the site being served.","solutions":["Update the opencli adapter to the latest version so buildAttractionExtractJs matches the current site markup.","Open the place URL in a normal browser and compare the DOM against the selectors used by buildAttractionExtractJs in clis/ctrip/utils.js.","Retry — a rare mid-load evaluate can return an unexpected value.","If you maintain the code, add logging of the raw evaluate result and patch the extract script's selectors."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isRowArray(v) { return Array.isArray(v) && v.every(r => r && typeof r === 'object'); }","tryCatchPattern":"try {\n  const rows = await opencli.ctrip.attraction(cityId);\n} catch (err) {\n  if (err.message.includes('malformed rows')) {\n    // site layout likely changed; update the adapter and/or retry later\n  }\n  throw err;\n}","preventionTips":["Keep the opencli adapter on the latest version to track Ctrip markup changes.","Retry once — mid-load evaluates can return unexpected values.","Compare live DOM against extraction selectors when the error repeats.","Pin to a known-good adapter version until fixes land."],"tags":["scraping","dom-parsing","schema-changed","ctrip"],"backgroundTag":"scraper-selector-drift","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}