jackwener/OpenCLI · error · TimeoutError
Ctrip flight API capture
Error message
Ctrip flight API capture
What it means
A TimeoutError raised when no batchSearch network response was captured within CAPTURE_TIMEOUT_SECONDS (12s) after opening the results page. parseBatchSearchCaptures returns null when the capture log contains no entry whose URL includes '/international/search/api/search/batchSearch', and the command converts that into this timeout so callers know the structured API response never arrived.
Source
Thrown at clis/ctrip/flight.js:196
`https://flights.ctrip.com/online/list/oneway-${fromCode.toLowerCase()}-${toCode.toLowerCase()}` +
`?depdate=${date}&cabin=Y_S_C_F&adult=1&child=0&infant=0`;
if (typeof page?.startNetworkCapture !== 'function' ||
typeof page?.readNetworkCapture !== 'function' ||
!await page.startNetworkCapture(CAPTURE_PATTERN)) {
throw new CommandExecutionError('Ctrip flight requires browser response interception');
}
await page.readNetworkCapture();
await page.goto(searchUrl);
// The initial document can finish before the large batchSearch body.
// The first rendered card is only a readiness signal; row data still
// comes exclusively from the structured response below.
const readiness = await page.evaluate(WAIT_FOR_BATCH_CAPTURE_JS);
if (readiness === 'captcha') {
throw new AuthRequiredError('flights.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
const itineraries = parseBatchSearchCaptures(await page.readNetworkCapture());
if (!itineraries) {
throw new TimeoutError('Ctrip flight API capture', CAPTURE_TIMEOUT_SECONDS, 'No batchSearch response was observed after opening the results page.');
}
if (itineraries.length === 0) {
throw new EmptyResultError('ctrip flight', `No flights for ${fromCode}→${toCode} on ${date}`);
}
const rows = itineraries
.map((itinerary, index) => mapItinerary(itinerary, searchUrl, index))
// The page groups direct flights before transfers, then applies its
// displayed starting price and departure-time order inside each group.
.sort(([rowA, connectingA, departureA, idA], [rowB, connectingB, departureB, idB]) =>
Number(connectingA) - Number(connectingB) || rowA.price - rowB.price ||
departureA.localeCompare(departureB) || idA.localeCompare(idB))
.slice(0, limit)
.map(([row], index) => ({
rank: index + 1,
airline: row.airline,
flightNo: row.flightNo,
aircraft: row.aircraft,
departureTime: row.departureTime,View on GitHub (pinned to 49907e53dc)
Solutions
- Retry — transient slowness or a stuck page is the most common cause.
- Increase CAPTURE_TIMEOUT_SECONDS if your network routinely takes >12s for the large batchSearch payload.
- Verify the capture pattern still matches by checking the Network tab for the search XHR; update CAPTURE_PATTERN if Ctrip changed the endpoint.
- Confirm the page isn't being soft-blocked (no flight cards rendered either) and, if so, treat it as an anti-bot issue: use a trusted session/IP.
Example fix
// before const CAPTURE_TIMEOUT_SECONDS = 12; // after: tolerate slow networks const CAPTURE_TIMEOUT_SECONDS = 30;
Defensive patterns
Strategy: retry
Try / catch
try {
const rows = await ctripFlight({ from, to, date });
} catch (e) {
if (e instanceof TimeoutError && e.message.includes('Ctrip flight API capture')) {
return retryWithBackoff(() => ctripFlight({ from, to, date }), { attempts: 2, baseMs: 3000 });
}
throw e;
} Prevention
- Retry timeout runs once or twice before escalating — capture misses are often transient.
- Raise CAPTURE_TIMEOUT_SECONDS on slow networks.
- Verify CAPTURE_PATTERN still matches the live batchSearch endpoint after Ctrip deploys.
- Check for soft anti-bot blocks (no captcha text, no data) and switch sessions/IPs when suspected.
When it happens
Trigger: Ctrip never issues the batchSearch XHR (page stuck on a shell, JS errors, or the request URL pattern changed); the response arrives after the 12s window on a slow connection; startNetworkCapture missed the request because interception began late; the page navigated to a captcha/verify state without setting the 'captcha' readiness signal.
Common situations: Slow or throttled networks; regional Ctrip variants served under different API paths; heavy load days where Ctrip delays search; an A/B test replacing batchSearch with a new endpoint; anti-bot soft-block returning no data without an explicit captcha page.
Related errors
- FETCH_ERROR
- archive wayback request failed: ${error?.message || error}
- Bilibili creator comparison
- Bilibili relation modify did not verify ${expectedLabel}; la
- Boss API request failed: ${message}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/dcd03d001e9af7d5.
Report an issue: GitHub.