jackwener/OpenCLI · error · CommandExecutionError

Trip.com deals page did not render deal tiles

Error message

Trip.com deals page did not render deal tiles (state=${String(waitResult)})

What it means

CommandExecutionError thrown when the deals page finished loading but WAIT_FOR_DEALS_JS resolved with a state other than 'content' or 'captcha' (e.g. 'timeout' or 'empty'): the deal tiles never appeared. It indicates the wait selector never matched within its budget, so nothing can be extracted.

Solutions

  1. Read the state value in the message and retry — transient timeouts often succeed on a second run
  2. Increase network patience / rerun on a faster connection or less loaded hour
  3. Verify the browser session reaches the Top Deals hub manually to rule out redirects
  4. If persistent, update WAIT_FOR_DEALS_JS selectors to match current Trip.com markup

Example fix

// before
await page.goto(buildDealsUrl());
const waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);
// after (retry transient wait failures once)
await page.goto(buildDealsUrl());
let waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);
if (waitResult !== 'content' && waitResult !== 'captcha') {
    await page.waitForTimeout(2000);
    waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);
}
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the deals hub URL loads and tiles exist before extraction
const ready = await page.evaluate(() =>
    document.querySelectorAll('[class*=deal-tile], [data-deal-id]').length > 0);
if (!ready) console.warn('Deal tiles not present yet; may need a longer wait');

Type guard

function isWaitState(v) {
    return typeof v === 'string' && ['content', 'captcha', 'timeout', 'empty'].includes(v);
}

Try / catch

try {
    await runTripDeals();
} catch (e) {
    if (e instanceof CommandExecutionError && /state=/.test(e.message)) {
        // read state from message; retry once with a longer budget before surfacing
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling the Trip.com deals command where page.evaluate(WAIT_FOR_DEALS_JS) returns any state string that is not 'content' (and not 'captcha') — the message embeds that state, e.g. (state=timeout).

Common situations: Slow network or page still loading assets; Trip.com serving a degraded/redirected page; region-specific page variants without the expected tiles; selector no longer matching after a site update.

Related errors


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

Appendix: source

Thrown at clis/trip/deals.js:42

    args: [
        { name: 'limit', type: 'int', default: 20, help: 'Number of deals (1-50)' },
    ],
    columns: [
        'rank',
        'title', 'offer',
        'discount',
        'url',
    ],
    func: async (page, kwargs) => {
        const limit = parseListLimit(kwargs.limit);

        await page.goto(buildDealsUrl());
        const waitResult = await page.evaluate(WAIT_FOR_DEALS_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 deals page did not render deal tiles (state=${String(waitResult)})`);
        }
        const raw = await page.evaluate(buildDealsExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Trip.com deals DOM extraction returned malformed rows');
        }
        // The Top Deals hub is a permanent curated page, so once the wait confirms it
        // rendered, zero parsed tiles means the tile markup drifted, not an empty hub.
        if (raw.length === 0) {
            throw new CommandExecutionError('Trip.com deals hub rendered but no promotion tiles parsed (the tile markup may have changed)');
        }
        return raw.slice(0, limit).map((r, i) => ({
            rank: i + 1,
            title: r.title,
            offer: r.offer,
            discount: r.discount,
            url: r.url,
        }));
    },

View on GitHub (pinned to 49907e53dc)