{"record":{"id":"4bc04c60baf97ae7","repo":"jackwener/OpenCLI","slug":"ctrip-flight-api-failed-status-string-payload","errorCode":null,"errorMessage":"Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'}","messagePattern":"Ctrip flight API failed \\(status=(.+?)\\): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/flight.js","lineNumber":88,"sourceCode":"        }\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    }\n    return [...byId.values()];\n}\n","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight.js#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (stale session retrying blindly)\nconst rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);\n// after (fresh session / verify manually on persistent failure)\ntry {\n  const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);\n} catch (err) {\n  if (String(err.message).includes('Ctrip flight API failed')) {\n    await browser.newProfileContext(); // drop cookies, get a fresh Ctrip session\n    const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);\n  }\n}","handlingStrategy":"try-catch","validationCode":"// cannot be pre-validated client-side; verify inputs are well-formed first\nif (!parseIataCode(from) || !parseIataCode(to) || !parseIsoDate(date)) {\n  throw new Error('invalid route/date arguments; fix before invoking the flight search');\n}","typeGuard":"function isBatchSearchOk(payload) {\n  return !!payload && typeof payload === 'object' && payload.status === 0;\n}","tryCatchPattern":"try {\n  const rows = await cli.run(['ctrip', 'flight', from, to, date]);\n} catch (err) {\n  const m = String(err.message).match(/Ctrip flight API failed \\(status=(\\S+)\\): (.*)/);\n  if (m) console.error(`Ctrip business error ${m[1]}: ${m[2]} — refresh session and retry`);\n  else throw err;\n}","preventionTips":["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"],"tags":["api","upstream","network","third-party"],"backgroundTag":"upstream-api-business-error","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}