{"record":{"id":"ed8bfc0df39d3e16","repo":"jackwener/OpenCLI","slug":"trip-com-package-search-returned-invalid-json-e","errorCode":null,"errorMessage":"Trip.com package search returned invalid JSON: ${err instanceof Error ? err.message : String(err)}","messagePattern":"Trip\\.com package search returned invalid JSON: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/utils.js","lineNumber":964,"sourceCode":"    };\n    let response;\n    try {\n        response = await fetch(PACKAGE_SEARCH_ENDPOINT, {\n            method: 'POST',\n            headers: { 'content-type': 'application/json', currency: 'USD' },\n            body: JSON.stringify(body),\n        });\n    } catch (err) {\n        throw new CommandExecutionError(`Trip.com package search fetch failed: ${err instanceof Error ? err.message : String(err)}`);\n    }\n    if (!response.ok) {\n        throw new CommandExecutionError(`Trip.com package search failed with status ${response.status}`);\n    }\n    let payload;\n    try {\n        payload = await response.json();\n    } catch (err) {\n        throw new CommandExecutionError(`Trip.com package search returned invalid JSON: ${err instanceof Error ? err.message : String(err)}`);\n    }\n    if (!Array.isArray(payload?.grouplist)) {\n        throw new CommandExecutionError('Trip.com package search returned malformed payload: missing grouplist array');\n    }\n    return payload.grouplist;\n}\n\n/**\n * Project a package flight group into the stable adapter column shape. A group's\n * `flightlist` is the itinerary legs (one for a nonstop), so the route summary\n * reads the departure off the first leg and the arrival off the last, with the\n * leg count minus one as the stop count. `price` is the per-person package\n * starting fare (`policylist[0].price.price`); missing values stay `null`.\n */\nexport function mapPackageRow(group, index) {\n    const legs = Array.isArray(group?.flightlist) ? group.flightlist : [];\n    const first = legs[0] || {};\n    const last = legs[legs.length - 1] || {};","sourceCodeStart":946,"sourceCodeEnd":982,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/utils.js#L946-L982","documentation":"fetchPackageSearch calls response.json() and re-throws parse failures as CommandExecutionError with 'Trip.com package search returned invalid JSON: ...'. The HTTP response was received (status checked) but the body is not valid JSON — typically an HTML page, an empty body, or a truncated stream.","triggerScenarios":"Trip.com serving an HTML anti-bot/challenge page with 200 status; response body truncated mid-transfer; empty body on unusual statuses; proxy injecting HTML error pages into the response stream.","commonSituations":"Anti-bot interstitials on datacenter IPs; corporate proxies replacing error responses with HTML; unstable connections truncating large package-search payloads; CDN caching anomalous responses.","solutions":["Fetch the endpoint with curl to see the raw body and confirm what is returned","Look for anti-bot/challenge HTML — switch headers, add cookies, or change egress IP","Retry to rule out transient truncation","Route through a debug proxy to capture the exact bytes and headers received"],"exampleFix":"// before\nconst groups = await fetchPackageSearch(body) // HTML body -> JSON.parse throws\n// after\ntry { const groups = await fetchPackageSearch(body) }\ncatch (e) { if (String(e.message).includes('invalid JSON')) log('got non-JSON body, likely anti-bot page'); throw e }","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"function hasGroupList(payload) {\n  return payload !== null && typeof payload === 'object' && Array.isArray(payload.grouplist);\n}","tryCatchPattern":"try {\n  const groups = await searchPackages(params);\n} catch (e) {\n  if (/package search returned invalid JSON/.test(e.message)) {\n    console.error('Non-JSON body from package search — check for anti-bot HTML or proxy injection.');\n    return retryAfterDelay(() => searchPackages(params), 5000);\n  }\n  throw e;\n}","preventionTips":["Inspect raw bodies with curl to detect anti-bot pages early","Retry on truncation; treat persistent HTML bodies as a blocking/egress issue","Use a debug proxy to capture exact responses when diagnosing","Watch for CDN/anomaly pages appearing with 200 status"],"tags":["http","json-parsing","api-response"],"backgroundTag":"invalid-json-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}