jackwener/OpenCLI · error · CommandExecutionError
Ctrip flight API returned an itinerary without an id
Error message
Ctrip flight API returned an itinerary without an id
What it means
Thrown when an itinerary inside payload.data.flightItineraryList has no non-empty itineraryId after trimming. The library deduplicates itineraries in a Map keyed by itineraryId, so an item without an id cannot be processed and is treated as a malformed record.
Source
Thrown at clis/ctrip/flight.js:96
throw new CommandExecutionError('Ctrip flight API response body was unavailable');
}
let payload;
try {
payload = JSON.parse(entry.responsePreview);
}
catch {
throw new CommandExecutionError('Ctrip flight API returned invalid JSON');
}
if (payload?.status !== 0) {
throw new CommandExecutionError(`Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'}`);
}
const itineraries = payload?.data?.flightItineraryList;
if (!Array.isArray(itineraries) || typeof payload?.data?.context?.finished !== 'boolean') {
throw new CommandExecutionError('Ctrip flight API returned a malformed batchSearch payload');
}
for (const itinerary of itineraries) {
const id = cleanString(itinerary?.itineraryId);
if (!id) throw new CommandExecutionError('Ctrip flight API returned an itinerary without an id');
byId.set(id, itinerary);
}
finished = payload.data.context.finished;
}
if (!finished) {
throw new CommandExecutionError('Ctrip flight batchSearch ended before the upstream search reported completion');
}
return [...byId.values()];
}
function mapItinerary(itinerary, searchUrl, index) {
const segments = itinerary?.flightSegments;
const prices = itinerary?.priceList;
if (!Array.isArray(segments) || segments.length === 0 || !Array.isArray(prices) || prices.length === 0) {
throw new CommandExecutionError(`Ctrip flight API returned malformed itinerary at index ${index}`);
}
const legs = segments.flatMap((segment) => Array.isArray(segment?.flightList) ? segment.flightList : []);
const first = legs[0];View on GitHub (pinned to 49907e53dc)
Solutions
- Dump responsePreview and inspect the offending itinerary object to confirm which field now holds the id.
- Retry the search — placeholder entries are often transient; a fresh query may return clean records.
- Update parsing to read the new id field name if Ctrip renamed itineraryId.
- As a workaround, filter out id-less itineraries before processing if you control the calling layer upstream of this parser.
Example fix
// before
for (const itinerary of itineraries) {
const id = cleanString(itinerary?.itineraryId);
if (!id) throw new CommandExecutionError('Ctrip flight API returned an itinerary without an id');
// after (tolerate id-less placeholder entries)
for (const itinerary of itineraries) {
const id = cleanString(itinerary?.itineraryId) || cleanString(itinerary?.itineraryID);
if (!id) continue; // skip placeholder entries instead of failing the whole search Defensive patterns
Strategy: validation
Validate before calling
// validate each itinerary has an id before processing
const valid = (payload?.data?.flightItineraryList || []).every(
(it) => typeof it?.itineraryId === 'string' && it.itineraryId.trim() !== ''
);
if (!valid) console.warn('response contains id-less itineraries; expect this error'); Type guard
function hasItineraryId(itinerary) {
return typeof itinerary?.itineraryId === 'string' && itinerary.itineraryId.trim().length > 0;
} Try / catch
try {
const rows = await cli.run(['ctrip', 'flight', from, to, date]);
} catch (err) {
if (String(err.message).includes('itinerary without an id')) {
console.error('Ctrip returned a placeholder itinerary; retry the search');
} else throw err;
} Prevention
- Retry once automatically — id-less entries are often transient placeholders
- Keep the parser updated if Ctrip renames itineraryId
- Prefer smaller limit values to reduce degraded result entries
- Log raw payloads when this fires to identify the new id field
When it happens
Trigger: A captured batchSearch response (status=0, valid array shape) contains at least one itinerary object where itineraryId is missing, null, empty string, or whitespace-only — often a placeholder/degraded entry Ctrip emits for sold-out or unpriceable flights.
Common situations: Seen when Ctrip includes ghost/placeholder itineraries in results, during schema changes that rename itineraryId, or when heavy automation traffic causes Ctrip to degrade some result entries.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Ctrip flight API returned a malformed batchSearch payload
- Ctrip flight API returned malformed itinerary at index ${ind
- eastmoney convertible returned malformed ${field} for ${bond
- archive snapshots returned malformed CDX payload: snapshot r
- archive snapshots returned malformed CDX payload: snapshot r
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ce7d1e5d81a35480.
Report an issue: GitHub.