{"record":{"id":"b8c1fda34161b8c9","repo":"jackwener/OpenCLI","slug":"trip-com-deals-page-did-not-render-deal-tiles-sta","errorCode":null,"errorMessage":"Trip.com deals page did not render deal tiles (state=${String(waitResult)})","messagePattern":"Trip\\.com deals page did not render deal tiles \\(state=(.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/deals.js","lineNumber":42,"sourceCode":"    args: [\n        { name: 'limit', type: 'int', default: 20, help: 'Number of deals (1-50)' },\n    ],\n    columns: [\n        'rank',\n        'title', 'offer',\n        'discount',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const limit = parseListLimit(kwargs.limit);\n\n        await page.goto(buildDealsUrl());\n        const waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('trip.com', 'Trip.com is asking for a verification; complete it in your browser session and retry');\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Trip.com deals page did not render deal tiles (state=${String(waitResult)})`);\n        }\n        const raw = await page.evaluate(buildDealsExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Trip.com deals DOM extraction returned malformed rows');\n        }\n        // The Top Deals hub is a permanent curated page, so once the wait confirms it\n        // rendered, zero parsed tiles means the tile markup drifted, not an empty hub.\n        if (raw.length === 0) {\n            throw new CommandExecutionError('Trip.com deals hub rendered but no promotion tiles parsed (the tile markup may have changed)');\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            title: r.title,\n            offer: r.offer,\n            discount: r.discount,\n            url: r.url,\n        }));\n    },","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/deals.js#L24-L60","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Read the state value in the message and retry — transient timeouts often succeed on a second run","Increase network patience / rerun on a faster connection or less loaded hour","Verify the browser session reaches the Top Deals hub manually to rule out redirects","If persistent, update WAIT_FOR_DEALS_JS selectors to match current Trip.com markup"],"exampleFix":"// before\nawait page.goto(buildDealsUrl());\nconst waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);\n// after (retry transient wait failures once)\nawait page.goto(buildDealsUrl());\nlet waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);\nif (waitResult !== 'content' && waitResult !== 'captcha') {\n    await page.waitForTimeout(2000);\n    waitResult = await page.evaluate(WAIT_FOR_DEALS_JS);\n}","handlingStrategy":"retry","validationCode":"// Confirm the deals hub URL loads and tiles exist before extraction\nconst ready = await page.evaluate(() =>\n    document.querySelectorAll('[class*=deal-tile], [data-deal-id]').length > 0);\nif (!ready) console.warn('Deal tiles not present yet; may need a longer wait');","typeGuard":"function isWaitState(v) {\n    return typeof v === 'string' && ['content', 'captcha', 'timeout', 'empty'].includes(v);\n}","tryCatchPattern":"try {\n    await runTripDeals();\n} catch (e) {\n    if (e instanceof CommandExecutionError && /state=/.test(e.message)) {\n        // read state from message; retry once with a longer budget before surfacing\n    } else { throw e; }\n}","preventionTips":["Retry transient wait failures with backoff before failing","Increase the wait budget on slow networks","Monitor Trip.com for redirect/regional variants that lack the standard hub"],"tags":["scraping","page-load-timeout","dom-parsing","trip-com"],"backgroundTag":"page-render-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}