jackwener/OpenCLI · error · CommandExecutionError

Trip.com hotel detail SSR extraction returned malformed data

Error message

Trip.com hotel detail SSR extraction returned malformed data

What it means

This CommandExecutionError is thrown when buildHotelDetailExtractJs() executed in the page but returned null or a non-object — the SSR data was present enough to pass the wait check, yet the extraction step could not produce a structured detail object. It indicates a mismatch between the extraction script and Trip.com's actual page payload.

Source

Thrown at clis/trip/hotel.js:47

        'star', 'score', 'scoreLabel', 'reviewCount', 'ratingBreakdown',
        'facilities', 'checkInOut',
        'cityName', 'address', 'lat', 'lon',
        'url',
    ],
    func: async (page, kwargs) => {
        const hotelId = parseHotelId('id', kwargs.id);
        const url = buildHotelDetailUrl(hotelId);
        await page.goto(url);
        const waitResult = await page.evaluate(WAIT_FOR_HOTEL_DETAIL_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('trip.com', 'Trip.com is asking for a verification; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Trip.com hotel detail page did not expose SSR hotel data (state=${String(waitResult)})`);
        }
        const detail = await page.evaluate(buildHotelDetailExtractJs());
        if (!detail || typeof detail !== 'object') {
            throw new CommandExecutionError('Trip.com hotel detail SSR extraction returned malformed data');
        }
        if (!detail.hotelId || !detail.name) {
            throw new EmptyResultError('trip hotel', `No detail exposed for hotel id ${hotelId}`);
        }
        return [{ ...detail, url }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry — partial renders can pass the wait check before the payload is complete
  2. Inspect the page payload in a browser and update the extraction script to the new SSR shape
  3. Pin or update the library version matching the current Trip.com page structure
  4. Add a guard so early/partial renders trigger an explicit re-wait instead of extraction

Example fix

// before
const detail = await tripHotelDetail({ id }); // SSR shape changed, returns malformed
// after
// update library so buildHotelDetailExtractJs reads the new SSR key
const detail = await tripHotelDetail({ id }); // works with updated extractor
Defensive patterns

Strategy: try-catch

Type guard

function isHotelDetail(d) {
  return d !== null && typeof d === 'object' && !Array.isArray(d);
}

Try / catch

try {
  const detail = await tripHotelDetail({ id });
} catch (e) {
  if (e instanceof CommandExecutionError && e.message.includes('malformed')) {
    // extraction/schema mismatch — flag for extractor update, skip item
    logger.warn({ hotelId: id }, 'trip SSR extraction failed');
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: page.evaluate(buildHotelDetailExtractJs()) returns null/undefined/a primitive because the expected SSR JSON structure changed, was truncated, or the extraction selectors no longer match the DOM.

Common situations: Trip.com renaming or reshaping its embedded SSR state, partial page renders where the wait heuristic passed early, or A/B-tested layouts lacking expected fields.

Understand the failure class

Related errors


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