jackwener/OpenCLI · error · CommandExecutionError
Trip.com package search returned invalid JSON: ${err instanc
Error message
Trip.com package search returned invalid JSON: ${err instanceof Error ? err.message : String(err)} What it means
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.
Source
Thrown at clis/trip/utils.js:964
};
let response;
try {
response = await fetch(PACKAGE_SEARCH_ENDPOINT, {
method: 'POST',
headers: { 'content-type': 'application/json', currency: 'USD' },
body: JSON.stringify(body),
});
} catch (err) {
throw new CommandExecutionError(`Trip.com package search fetch failed: ${err instanceof Error ? err.message : String(err)}`);
}
if (!response.ok) {
throw new CommandExecutionError(`Trip.com package search failed with status ${response.status}`);
}
let payload;
try {
payload = await response.json();
} catch (err) {
throw new CommandExecutionError(`Trip.com package search returned invalid JSON: ${err instanceof Error ? err.message : String(err)}`);
}
if (!Array.isArray(payload?.grouplist)) {
throw new CommandExecutionError('Trip.com package search returned malformed payload: missing grouplist array');
}
return payload.grouplist;
}
/**
* Project a package flight group into the stable adapter column shape. A group's
* `flightlist` is the itinerary legs (one for a nonstop), so the route summary
* reads the departure off the first leg and the arrival off the last, with the
* leg count minus one as the stop count. `price` is the per-person package
* starting fare (`policylist[0].price.price`); missing values stay `null`.
*/
export function mapPackageRow(group, index) {
const legs = Array.isArray(group?.flightlist) ? group.flightlist : [];
const first = legs[0] || {};
const last = legs[legs.length - 1] || {};View on GitHub (pinned to 49907e53dc)
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
Example fix
// before
const groups = await fetchPackageSearch(body) // HTML body -> JSON.parse throws
// after
try { const groups = await fetchPackageSearch(body) }
catch (e) { if (String(e.message).includes('invalid JSON')) log('got non-JSON body, likely anti-bot page'); throw e } Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
function hasGroupList(payload) {
return payload !== null && typeof payload === 'object' && Array.isArray(payload.grouplist);
} Try / catch
try {
const groups = await searchPackages(params);
} catch (e) {
if (/package search returned invalid JSON/.test(e.message)) {
console.error('Non-JSON body from package search — check for anti-bot HTML or proxy injection.');
return retryAfterDelay(() => searchPackages(params), 5000);
}
throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Anti-bot interstitials on datacenter IPs; corporate proxies replacing error responses with HTML; unstable connections truncating large package-search payloads; CDN caching anomalous responses.
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
- coingecko global returned malformed JSON: ${err?.message ??
- Trip.com poiSearch returned invalid JSON: ${err instanceof E
- 12306 queryByTrainNo returned non-JSON body
- 12306 ${endpoint} returned non-JSON body
- coingecko derivatives returned malformed JSON: ${err?.messag
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ed8bfc0df39d3e16.
Report an issue: GitHub.