jackwener/OpenCLI · error · CommandExecutionError

Ctrip package DOM extraction returned malformed rows

Error message

Ctrip package DOM extraction returned malformed rows

What it means

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).

Source

Thrown at clis/ctrip/package.js:57

    func: async (page, kwargs) => {
        const destination = parsePlaceName('destination', kwargs.destination);
        const limit = parseListLimit(kwargs.limit);

        const searchUrl = buildPackageListUrl(destination);
        await page.goto(searchUrl);
        const waitResult = await page.evaluate(WAIT_FOR_VACATIONS_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('vacations.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult === 'empty') {
            throw new EmptyResultError('ctrip package', `No flight-plus-hotel packages for "${destination}"`);
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip package page did not render package cards (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(buildVacationsExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip package DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip package cards rendered but parser did not find required package anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            subtitle: r.subtitle,
            tags: r.tags,
            score: r.score,
            sold: r.sold,
            reviews: r.reviews,
            price: r.price,
            url: searchUrl,
        }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run once — a page-side script error can be transient
  2. Update the CLI to the latest version so buildVacationsExtractJs matches current Ctrip DOM
  3. Open the search URL in a browser devtools console and run the extract script manually to see its actual return value
  4. Check for page console errors during extraction (script may be clobbered by site JS)
  5. If you maintain the extractor, wrap collection in Array.from(...) and defensively return []

Example fix

// before
const raw = await page.evaluate(buildVacationsExtractJs());
if (!Array.isArray(raw)) throw new CommandExecutionError('...malformed rows');
// after
const raw = await page.evaluate(buildVacationsExtractJs());
const rows = Array.isArray(raw) ? raw : [];
if (rows.length === 0) throw new EmptyResultError('ctrip package', 'No packages extracted');
Defensive patterns

Strategy: try-catch

Type guard

function isRowArray(v) {
  return Array.isArray(v) && v.every((r) => r && typeof r === 'object');
}

Try / catch

try {
  const rows = await runCli('ctrip', 'package', dest);
} catch (e) {
  if (e.name === 'CommandExecutionError' && e.message.includes('malformed rows')) {
    // fall back to re-run or report extractor bug
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/ec9bc66cfcb7b2fd. Report an issue: GitHub.