jackwener/OpenCLI · error · CommandExecutionError
Ctrip flight API failed
Error message
Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'} What it means
This error is thrown by parseBatchSearchCaptures when the captured Ctrip batchSearch HTTP response body parses to JSON whose top-level `status` field is not 0. Ctrip's API signals business-level success with status===0; any other value (with an optional human-readable `msg`) means the upstream search request was rejected or failed. The library surfaces the upstream status code and message verbatim so the developer can see what Ctrip complained about.
Solutions
- Read the upstream `msg` in the error message — it names the concrete Ctrip business failure and usually dictates the fix.
- Retry the search in a fresh browser session / clear cookies so Ctrip issues a new session and token.
- Re-run interactively in a real browser first; if a captcha or verification appears, complete it, then retry the command.
- If msg indicates invalid parameters, adjust route/date arguments (valid IATA codes, ISO dates, present/future dates).
- If it persists, check whether Ctrip changed the batchSearch API contract and update the capture pattern/parsing in clis/ctrip/flight.js.
Example fix
// before (stale session retrying blindly)
const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
// after (fresh session / verify manually on persistent failure)
try {
const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
} catch (err) {
if (String(err.message).includes('Ctrip flight API failed')) {
await browser.newProfileContext(); // drop cookies, get a fresh Ctrip session
const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// cannot be pre-validated client-side; verify inputs are well-formed first
if (!parseIataCode(from) || !parseIataCode(to) || !parseIsoDate(date)) {
throw new Error('invalid route/date arguments; fix before invoking the flight search');
} Type guard
function isBatchSearchOk(payload) {
return !!payload && typeof payload === 'object' && payload.status === 0;
} Try / catch
try {
const rows = await cli.run(['ctrip', 'flight', from, to, date]);
} catch (err) {
const m = String(err.message).match(/Ctrip flight API failed \(status=(\S+)\): (.*)/);
if (m) console.error(`Ctrip business error ${m[1]}: ${m[2]} — refresh session and retry`);
else throw err;
} Prevention
- Use a fresh, logged-in browser profile so the Ctrip session and tokens are valid
- Complete any captcha/verification manually before automating
- Validate route (IATA) and date arguments before running the search
- Throttle request frequency to avoid Ctrip risk control
- Log the full upstream msg from the error for diagnosis
When it happens
Trigger: The browser-captured POST to /international/search/api/search/batchSearch returns HTTP 200 but payload.status !== 0 — e.g. expired/invalid Ctrip session cookies, risk-control rejection, invalid search parameters forwarded by the page, or Ctrip-side throttling/business errors.
Common situations: Developers hit this when reusing a stale browser profile whose Ctrip session expired, when Ctrip's anti-bot risk control flags the automated browser, when querying routes/dates Ctrip considers invalid, or during Ctrip API contract changes that shift the status field semantics.
Related errors
- API_ERROR
- arXiv API HTTP
- Ctrip flight API returned a malformed batchSearch payload
- Ctrip flight API returned an itinerary without an id
- Ctrip flight batchSearch ended before the upstream search…
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4bc04c60baf97ae7.
Report an issue: GitHub.
Appendix: source
Thrown at clis/ctrip/flight.js:88
}
if (status !== 200) {
throw new CommandExecutionError(`Ctrip flight API returned HTTP ${status || 'unknown'}`);
}
if (entry?.responseBodyTruncated === true) {
throw new CommandExecutionError('Ctrip flight API response exceeded the browser capture limit');
}
if (typeof entry?.responsePreview !== 'string') {
throw new CommandExecutionError('Ctrip flight API response body was unavailable');
}
let payload;
try {
payload = JSON.parse(entry.responsePreview);
}
catch {
throw new CommandExecutionError('Ctrip flight API returned invalid JSON');
}
if (payload?.status !== 0) {
throw new CommandExecutionError(`Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'}`);
}
const itineraries = payload?.data?.flightItineraryList;
if (!Array.isArray(itineraries) || typeof payload?.data?.context?.finished !== 'boolean') {
throw new CommandExecutionError('Ctrip flight API returned a malformed batchSearch payload');
}
for (const itinerary of itineraries) {
const id = cleanString(itinerary?.itineraryId);
if (!id) throw new CommandExecutionError('Ctrip flight API returned an itinerary without an id');
byId.set(id, itinerary);
}
finished = payload.data.context.finished;
}
if (!finished) {
throw new CommandExecutionError('Ctrip flight batchSearch ended before the upstream search reported completion');
}
return [...byId.values()];
}
View on GitHub (pinned to 49907e53dc)