jackwener/OpenCLI · error · CommandExecutionError

Ctrip attraction DOM extraction returned malformed rows

Error message

Ctrip attraction DOM extraction returned malformed rows

What it means

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.

Source

Thrown at clis/ctrip/attraction.js:60

        'rating', 'reviews',
        'url',
    ],
    func: async (page, kwargs) => {
        const cityId = parseCityId(kwargs.city);
        const limit = parseListLimit(kwargs.limit);

        const placeUrl = buildAttractionPlaceUrl(cityId);
        await page.goto(placeUrl);
        const waitResult = await page.evaluate(buildWaitForAttractionsJs(cityId));
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('you.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip place page did not render attraction links for city id ${cityId} (state=${String(waitResult)}); check the city id`);
        }
        const raw = await page.evaluate(buildAttractionExtractJs(cityId));
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip attraction DOM extraction returned malformed rows');
        }
        if (raw.length === 0) {
            throw new CommandExecutionError('Ctrip attraction links rendered but parser did not find required sight anchors');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            name: r.name,
            rating: r.rating,
            reviews: r.reviews,
            url: r.url,
        }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the opencli adapter to the latest version so buildAttractionExtractJs matches the current site markup.
  2. Open the place URL in a normal browser and compare the DOM against the selectors used by buildAttractionExtractJs in clis/ctrip/utils.js.
  3. Retry — a rare mid-load evaluate can return an unexpected value.
  4. If you maintain the code, add logging of the raw evaluate result and patch the extract script's selectors.
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 opencli.ctrip.attraction(cityId);
} catch (err) {
  if (err.message.includes('malformed rows')) {
    // site layout likely changed; update the adapter and/or retry later
  }
  throw err;
}

Prevention

When it happens

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

Common situations: Ctrip updated the you.ctrip.com DOM structure; adapter version older than a recent site change; unusual regional variant of the site being served.

Understand the failure class

Related errors


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