{"record":{"id":"e9335d151b7446f5","repo":"jackwener/OpenCLI","slug":"ctrip-flight-api-returned-invalid-json","errorCode":null,"errorMessage":"Ctrip flight API returned invalid JSON","messagePattern":"Ctrip flight API returned invalid JSON","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/flight.js","lineNumber":85,"sourceCode":"        const status = Number(entry?.responseStatus || 0);\n        if (status === 401 || status === 403) {\n            throw new AuthRequiredError('flights.ctrip.com', `Ctrip flight API returned HTTP ${status}; complete any verification in the browser and retry`);\n        }\n        if (status !== 200) {\n            throw new CommandExecutionError(`Ctrip flight API returned HTTP ${status || 'unknown'}`);\n        }\n        if (entry?.responseBodyTruncated === true) {\n            throw new CommandExecutionError('Ctrip flight API response exceeded the browser capture limit');\n        }\n        if (typeof entry?.responsePreview !== 'string') {\n            throw new CommandExecutionError('Ctrip flight API response body was unavailable');\n        }\n        let payload;\n        try {\n            payload = JSON.parse(entry.responsePreview);\n        }\n        catch {\n            throw new CommandExecutionError('Ctrip flight API returned invalid JSON');\n        }\n        if (payload?.status !== 0) {\n            throw new CommandExecutionError(`Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'}`);\n        }\n        const itineraries = payload?.data?.flightItineraryList;\n        if (!Array.isArray(itineraries) || typeof payload?.data?.context?.finished !== 'boolean') {\n            throw new CommandExecutionError('Ctrip flight API returned a malformed batchSearch payload');\n        }\n        for (const itinerary of itineraries) {\n            const id = cleanString(itinerary?.itineraryId);\n            if (!id) throw new CommandExecutionError('Ctrip flight API returned an itinerary without an id');\n            byId.set(id, itinerary);\n        }\n        finished = payload.data.context.finished;\n    }\n    if (!finished) {\n        throw new CommandExecutionError('Ctrip flight batchSearch ended before the upstream search reported completion');\n    }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight.js#L67-L103","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: assume JSON\nconst data = JSON.parse(rawBody);\n// after: detect HTML challenge before parsing (pattern for callers wrapping the CLI)\ntry {\n  const data = JSON.parse(rawBody);\n} catch {\n  if (/<html|验证|captcha/i.test(rawBody)) throw new Error('Ctrip returned an HTML verification page; complete the challenge and retry');\n  throw new Error('Ctrip returned invalid JSON');\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isLikelyHtmlChallenge(text) {\n  return typeof text === 'string' && /<html|<body|captcha|验证|安全验证/i.test(text);\n}","tryCatchPattern":"try {\n  return await cli.itineraries(args);\n} catch (err) {\n  if (/invalid JSON/i.test(err.message || '')) {\n    // likely Ctrip served an HTML challenge/maintenance page with HTTP 200\n    await completeBrowserVerificationIfPresent();\n    return cli.itineraries(args);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["json","parse-error","anti-bot","html-response"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}