{"record":{"id":"9fd8b2fa71d59ea6","repo":"jackwener/OpenCLI","slug":"ctrip-cruise-port-page-did-not-render-state-str","errorCode":null,"errorMessage":"Ctrip cruise port page did not render (state=${String(portWait)})","messagePattern":"Ctrip cruise port page did not render \\(state=(.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/cruise.js","lineNumber":74,"sourceCode":"        }\n        const portCode = await page.evaluate(buildCruisePortLookupJs(port));\n        if (!portCode) {\n            throw new EmptyResultError('ctrip cruise', `No cruise departure port matching \"${port}\" (try a listed sea-cruise port like 上海 / 威尼斯 / 罗马)`);\n        }\n\n        let searchUrl = indexUrl;\n        if (portCode !== PORT_INDEX_CODE) {\n            searchUrl = buildCruiseSearchUrl(portCode);\n            await page.goto(searchUrl);\n            const portWait = await page.evaluate(WAIT_FOR_CRUISE_JS);\n            if (portWait === 'captcha') {\n                throw new AuthRequiredError('cruise.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');\n            }\n            if (portWait === 'empty') {\n                throw new EmptyResultError('ctrip cruise', `No cruises currently departing \"${port}\"`);\n            }\n            if (portWait !== 'content') {\n                throw new CommandExecutionError(`Ctrip cruise port page did not render (state=${String(portWait)})`);\n            }\n        }\n\n        const raw = await page.evaluate(buildCruiseExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip cruise DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            throw new CommandExecutionError('Ctrip cruise cards rendered but parser did not find required itinerary anchors');\n        }\n        return raw.slice(0, limit).map((r, i) => ({\n            rank: i + 1,\n            title: r.title,\n            star: r.star,\n            boarding: r.boarding,\n            sailingDate: r.sailingDate,\n            tags: r.tags,\n            price: r.price,","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/cruise.js#L56-L92","documentation":"CommandExecutionError thrown when the port's cruise.ctrip.com page neither rendered content nor showed the known empty state — WAIT_FOR_CRUISE_JS returned some other state (timeout, unknown selector state, navigation hiccup). The library cannot classify the page, so it fails with the raw wait state embedded in the message for diagnosis. It indicates a page-load/render problem rather than a data or auth problem.","triggerScenarios":"Calling `ctrip cruise <port>` where after page.goto on the port results URL the in-page probe times out or returns an unrecognized state (network slowness, SPA hydration failure, unexpected page markup, redirect to an error page).","commonSituations":"Slow or proxied network causing the wait timeout to expire before the SPA hydrates; Ctrip A/B-deploying new markup so the probe's known selectors vanish; transient 5xx or interstitial pages breaking navigation; headless environment where some resources never load.","solutions":["Read the state value in the message and rerun once — transient loads often succeed on retry","Increase the browser wait/navigation timeout used by the CLI before rerunning","Open buildCruiseSearchUrl(portCode) in a real browser to see what actually renders (captcha, error page, new layout)","Update the CLI (or WAIT_FOR_CRUISE_JS in clis/ctrip/utils.js) if Ctrip changed its page structure"],"exampleFix":"// before: single fragile attempt\nawait run(['ctrip', 'cruise', '威尼斯']);\n// after: one retry on render failure\ntry {\n  await run(['ctrip', 'cruise', '威尼斯']);\n} catch (e) {\n  if (e.name === 'CommandExecutionError' && /did not render/.test(e.message)) {\n    await sleep(3000);\n    await run(['ctrip', 'cruise', '威尼斯']);\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"// Pre-flight: probe network reachability and page load latency before running the command\nconst t0 = Date.now();\nawait fetch('https://cruise.ctrip.com/', { method: 'HEAD' });\nif (Date.now() - t0 > 5000) console.warn('Slow route to Ctrip; raise the CLI wait timeout');","typeGuard":"function isRenderFailure(e) {\n  return e instanceof Error && e.name === 'CommandExecutionError' && /did not render/.test(e.message);\n}","tryCatchPattern":"try {\n  return await run(['ctrip', 'cruise', port]);\n} catch (e) {\n  if (isRenderFailure(e)) {\n    await sleep(3000);\n    return run(['ctrip', 'cruise', port]); // one retry with backoff\n  }\n  throw e;\n}","preventionTips":["Run with a stable, low-latency network connection to Ctrip domains","Raise browser navigation/wait timeouts in slow or proxied environments","Keep the CLI updated so WAIT_FOR_CRUISE_JS tracks current Ctrip markup","Check the state= value in the message — consistent unknown states mean a markup change, not flakiness"],"tags":["render-failure","timeout","dom","ctrip","browser-automation"],"backgroundTag":"page-render-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}