{"record":{"id":"3e7b12391040d15e","repo":"jackwener/OpenCLI","slug":"ctrip-flight-api-returned-http-status-unknow","errorCode":null,"errorMessage":"Ctrip flight API returned HTTP ${status || 'unknown'}","messagePattern":"Ctrip flight API returned HTTP (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/flight.js","lineNumber":72,"sourceCode":"    return codes.length > 0 ? codes.map((code) => labels[code]).join('/') : (cleanString(value) || null);\n}\n\nfunction parseBatchSearchCaptures(entries) {\n    if (!Array.isArray(entries)) {\n        throw new CommandExecutionError('Ctrip flight network capture returned malformed entries');\n    }\n    const captured = entries.filter((entry) => String(entry?.url || '').includes(CAPTURE_PATTERN));\n    if (captured.length === 0) return null;\n\n    const byId = new Map();\n    let finished = false;\n    for (const entry of captured) {\n        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;","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight.js#L54-L90","documentation":"This CommandExecutionError is thrown when the captured batchSearch response returns an HTTP status that is neither 200 nor 401/403 (e.g. 429, 5xx, or an unknown status when responseStatus is missing). It signals the Ctrip API call itself failed at the transport level, so no flight data can be parsed. Status 0/undefined yields 'unknown', typically meaning the response never completed or the capture metadata was lost.","triggerScenarios":"Captured /international/search/api/search/batchSearch entry whose responseStatus is e.g. 429 (rate limited), 500/502/503 (Ctrip server error), or 0/missing (request aborted, navigation interrupted, or capture failed to record status).","commonSituations":"Hammering the CLI with repeated searches leading to 429 rate limiting; transient Ctrip server incidents (5xx); closing the browser or navigating away before the response completes (status unknown); CDP capture failing mid-request.","solutions":["Retry the search after a short backoff — 429 and 5xx are usually transient.","Check the reported status code: 429 means back off and slow the request rate; 5xx means wait for Ctrip to recover.","If status is 'unknown', keep the browser open and ensure the page stays on the results flow until the batchSearch response is captured.","Verify network/proxy stability between the automation browser and flights.ctrip.com."],"exampleFix":"// before: immediate retry\nconst rows = await cli.itineraries(args);\n// after: backoff on transient HTTP failures\ntry {\n  const rows = await cli.itineraries(args);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /HTTP (429|5\\d\\d)/.test(err.message)) {\n    await sleep(30_000);\n    return cli.itineraries(args);\n  }\n  throw err;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await cli.itineraries(args);\n} catch (err) {\n  const m = /HTTP (\\d+|'unknown')/.exec(err.message || '');\n  const status = m && m[1] !== 'unknown' ? Number(m[1]) : 0;\n  if (status === 429 || status >= 500) {\n    await sleep(backoffMs);\n    return cli.itineraries(args); // bounded retries\n  }\n  throw err;\n}","preventionTips":["Add exponential backoff and a retry cap for 429/5xx statuses.","Space out searches; do not run many routes in rapid succession.","Keep the browser/tab alive until the CLI completes so responses are not aborted (unknown status).","Monitor Ctrip status/incidents if 5xx errors cluster."],"tags":["http","rate-limit","network","server-error"],"backgroundTag":"unexpected-http-status","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}