{"record":{"id":"ec9bc66cfcb7b2fd","repo":"jackwener/OpenCLI","slug":"ctrip-package-dom-extraction-returned-malformed-ro","errorCode":null,"errorMessage":"Ctrip package DOM extraction returned malformed rows","messagePattern":"Ctrip package DOM extraction returned malformed rows","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/package.js","lineNumber":57,"sourceCode":"    func: async (page, kwargs) => {\n        const destination = parsePlaceName('destination', kwargs.destination);\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl = buildPackageListUrl(destination);\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_VACATIONS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('vacations.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');\n        }\n        if (waitResult === 'empty') {\n            throw new EmptyResultError('ctrip package', `No flight-plus-hotel packages for \"${destination}\"`);\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Ctrip package page did not render package cards (state=${String(waitResult)})`);\n        }\n        const raw = await page.evaluate(buildVacationsExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip package DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            throw new CommandExecutionError('Ctrip package cards rendered but parser did not find required package anchors');\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            title: r.title,\n            subtitle: r.subtitle,\n            tags: r.tags,\n            score: r.score,\n            sold: r.sold,\n            reviews: r.reviews,\n            price: r.price,\n            url: searchUrl,\n        }));\n    },\n});\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/package.js#L39-L75","documentation":"Raised by `ctrip package` when page.evaluate(buildVacationsExtractJs()) returns something other than an array. The extractor script is expected to always produce an array of raw package rows; a non-array means the in-page extraction script misbehaved or the page context corrupted its return value (e.g. non-serializable results, evaluate returning undefined on script error).","triggerScenarios":"Running `opencli ctrip package <destination>` where the page rendered but buildVacationsExtractJs() threw inside the browser (evaluate resolves to undefined) or returned a non-array due to a script defect, instead of an array of row objects.","commonSituations":"Ctrip page JS overriding globals the extractor relies on; a structural change making the extractor's collection code return null; an old CLI version whose extractor no longer matches the page; Playwright/Puppeteer serialization issues with large or cyclic results.","solutions":["Re-run once — a page-side script error can be transient","Update the CLI to the latest version so buildVacationsExtractJs matches current Ctrip DOM","Open the search URL in a browser devtools console and run the extract script manually to see its actual return value","Check for page console errors during extraction (script may be clobbered by site JS)","If you maintain the extractor, wrap collection in Array.from(...) and defensively return []"],"exampleFix":"// before\nconst raw = await page.evaluate(buildVacationsExtractJs());\nif (!Array.isArray(raw)) throw new CommandExecutionError('...malformed rows');\n// after\nconst raw = await page.evaluate(buildVacationsExtractJs());\nconst rows = Array.isArray(raw) ? raw : [];\nif (rows.length === 0) throw new EmptyResultError('ctrip package', 'No packages extracted');","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isRowArray(v) {\n  return Array.isArray(v) && v.every((r) => r && typeof r === 'object');\n}","tryCatchPattern":"try {\n  const rows = await runCli('ctrip', 'package', dest);\n} catch (e) {\n  if (e.name === 'CommandExecutionError' && e.message.includes('malformed rows')) {\n    // fall back to re-run or report extractor bug\n  } else throw e;\n}","preventionTips":["Keep the CLI extractor up to date with Ctrip DOM changes","Re-run once before reporting; in-page script errors can be transient","Avoid environments where site JS can clobber extractor globals","Monitor for repeated occurrences — signals a structural page change"],"tags":["browser-automation","dom-extraction","scraping","ctrip"],"backgroundTag":"dom-extraction-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}