jackwener/OpenCLI · error · CommandExecutionError
Ctrip flight API returned invalid JSON
Error message
Ctrip flight API returned invalid JSON
What it means
This CommandExecutionError is thrown when the captured batchSearch response body cannot be parsed with JSON.parse. The HTTP layer succeeded (status 200, body present, not truncated) but the body is not valid JSON — often an HTML error/challenge page, an anti-bot interstitial, or corrupted content served in place of the API response. The library fails fast rather than guessing at the payload shape.
Source
Thrown at clis/ctrip/flight.js:85
const status = Number(entry?.responseStatus || 0);
if (status === 401 || status === 403) {
throw new AuthRequiredError('flights.ctrip.com', `Ctrip flight API returned HTTP ${status}; complete any verification in the browser and retry`);
}
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');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the search; if the page showed a verification interstitial, complete it in the browser first.
- Log/inspect the responsePreview content to confirm whether it is an HTML challenge page or genuinely corrupt JSON.
- Check for proxies/anti-virus performing TLS interception between the browser and flights.ctrip.com and bypass them.
- Update the CLI — capture/decoding handling may have been fixed in a newer version.
Example fix
// before: assume JSON
const data = JSON.parse(rawBody);
// after: detect HTML challenge before parsing (pattern for callers wrapping the CLI)
try {
const data = JSON.parse(rawBody);
} catch {
if (/<html|验证|captcha/i.test(rawBody)) throw new Error('Ctrip returned an HTML verification page; complete the challenge and retry');
throw new Error('Ctrip returned invalid JSON');
} Defensive patterns
Strategy: try-catch
Type guard
function isLikelyHtmlChallenge(text) {
return typeof text === 'string' && /<html|<body|captcha|验证|安全验证/i.test(text);
} Try / catch
try {
return await cli.itineraries(args);
} catch (err) {
if (/invalid JSON/i.test(err.message || '')) {
// likely Ctrip served an HTML challenge/maintenance page with HTTP 200
await completeBrowserVerificationIfPresent();
return cli.itineraries(args);
}
throw err;
} Prevention
- Complete any in-browser verification before running scripted searches.
- Bypass TLS-intercepting proxies/AV that can rewrite response bodies.
- Retry after a short delay — WAF challenge pages are often transient.
- Log raw response content on this error to distinguish challenge pages from corruption.
When it happens
Trigger: JSON.parse throws on the captured responsePreview — Ctrip served an HTML page (CAPTCHA, login wall, maintenance page, WAF block) with a 200 status, or the body was corrupted during capture.
Common situations: Ctrip's WAF returning a 200 HTML challenge page instead of JSON; a CDN/maintenance interstitial during Ctrip deploys; proxy or TLS-interception middleware rewriting the response body; encoding issues from the CDP capture layer.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- PARSE_ERROR
- toutiao hot-board returned malformed JSON: ${error?.message
- Failed to parse 12306 station_name.js: source string not fou
- ${label} returned a non-JSON response
- Chess.com callback returned malformed JSON for ${url}: ${err
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e9335d151b7446f5.
Report an issue: GitHub.