{"record":{"id":"03074c7abd208fbb","repo":"jackwener/OpenCLI","slug":"trip-com-package-search-returned-groups-length","errorCode":null,"errorMessage":"Trip.com package search returned ${groups.length} group(s) but none carried a parseable flight identity, route, and time","messagePattern":"Trip\\.com package search returned (.+?) group\\(s\\) but none carried a parseable flight identity, route, and time","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/trip/package.js","lineNumber":89,"sourceCode":"        }\n\n        const groups = await fetchPackageSearch({\n            dcode: origin.cityCode,\n            acode: dest.cityCode,\n            hcityid: String(dest.cityId),\n            depart,\n            ret,\n            adults,\n        });\n        if (groups.length === 0) {\n            throw new EmptyResultError('trip package', `No flight+hotel packages for ${origin.name} to ${dest.name} on ${depart} .. ${ret}`);\n        }\n        const rows = groups\n            .filter((g) => g && Array.isArray(g.flightlist) && g.flightlist.length)\n            .map((g) => mapPackageRow(g, 0))\n            .filter((row) => row.flightNo && row.from && row.to && row.departure && row.arrival);\n        if (rows.length === 0) {\n            throw new CommandExecutionError(`Trip.com package search returned ${groups.length} group(s) but none carried a parseable flight identity, route, and time`);\n        }\n        return rows.slice(0, limit).map((row, i) => ({ ...row, rank: i + 1 }));\n    },\n});\n","sourceCodeStart":71,"sourceCodeEnd":94,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/package.js#L71-L94","documentation":"This CommandExecutionError signals schema drift: Trip.com returned one or more package groups, but after filtering and mapping, no row had a parseable flightNo, from, to, departure, and arrival. The library throws rather than returning partial/garbage rows, so you know the upstream markup/API shape changed rather than the route being empty.","triggerScenarios":"groups.length > 0 but every mapped row fails the row.flightNo && row.from && row.to && row.departure && row.arrival filter — i.e. mapPackageRow produced rows missing required fields, typically because Trip.com changed the flightlist item structure.","commonSituations":"Trip.com deploying a UI/API schema change; a group whose flightlist contains placeholder or partial entries; mapPackageRow expectations lagging a new field layout.","solutions":["Retry once — occasionally transient partial data; a fresh request may parse.","Inspect one raw group's flightlist and update mapPackageRow to the new field names/paths.","Check for a newer version of this CLI that already handles the updated Trip.com schema.","File/inspect a bug report with a captured raw response if the schema change persists."],"exampleFix":"// before (old mapPackageRow assumption)\nflightNo: g.flightlist[0].flightNo,\n// after (adapt to new nested shape found in captured response)\nflightNo: g.flightlist[0]?.flight?.flightNo ?? g.flightlist[0]?.flightNo,","handlingStrategy":"validation","validationCode":"const groups = await fetchGroups();\nif (Array.isArray(groups) && groups.length && !groups.some(g => Array.isArray(g.flightlist) && g.flightlist.length)) {\n  console.warn('Upstream schema may have changed: no flightlist in any group');\n}","typeGuard":"function isParseableRow(row) {\n  return Boolean(row && row.flightNo && row.from && row.to && row.departure && row.arrival);\n}","tryCatchPattern":"try {\n  const rows = await packageSearch(params);\n} catch (e) {\n  if (/parseable flight identity/.test(e.message)) {\n    // capture raw response for schema debugging, escalate as a bug\n  }\n  throw e;\n}","preventionTips":["Pin and monitor the CLI/upstream scraper version; update after Trip.com schema changes.","Add an integration test asserting a known route yields parseable rows.","Keep mapPackageRow defensive with optional chaining and fallbacks.","Alert on this error in production — it indicates drift, not user error."],"tags":["schema-drift","parsing","trip-com"],"backgroundTag":"schema-drift-upstream","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}