jackwener/OpenCLI · error · CommandExecutionError

Ctrip flight API failed (status=${String(payload?.status)}):

Error message

Ctrip flight API failed (status=${String(payload?.status)}): ${cleanString(payload?.msg) || 'unknown error'}

What it means

This error is thrown by parseBatchSearchCaptures when the captured Ctrip batchSearch HTTP response body parses to JSON whose top-level `status` field is not 0. Ctrip's API signals business-level success with status===0; any other value (with an optional human-readable `msg`) means the upstream search request was rejected or failed. The library surfaces the upstream status code and message verbatim so the developer can see what Ctrip complained about.

Source

Thrown at clis/ctrip/flight.js:88

        }
        if (status !== 200) {
            throw new CommandExecutionError(`Ctrip flight API returned HTTP ${status || 'unknown'}`);
        }
        if (entry?.responseBodyTruncated === true) {
            throw new CommandExecutionError('Ctrip flight API response exceeded the browser capture limit');
        }
        if (typeof entry?.responsePreview !== 'string') {
            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()];
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the upstream `msg` in the error message — it names the concrete Ctrip business failure and usually dictates the fix.
  2. Retry the search in a fresh browser session / clear cookies so Ctrip issues a new session and token.
  3. Re-run interactively in a real browser first; if a captcha or verification appears, complete it, then retry the command.
  4. If msg indicates invalid parameters, adjust route/date arguments (valid IATA codes, ISO dates, present/future dates).
  5. If it persists, check whether Ctrip changed the batchSearch API contract and update the capture pattern/parsing in clis/ctrip/flight.js.

Example fix

// before (stale session retrying blindly)
const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
// after (fresh session / verify manually on persistent failure)
try {
  const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
} catch (err) {
  if (String(err.message).includes('Ctrip flight API failed')) {
    await browser.newProfileContext(); // drop cookies, get a fresh Ctrip session
    const rows = await cli.run(['ctrip', 'flight', 'PEK', 'SHA', date]);
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// cannot be pre-validated client-side; verify inputs are well-formed first
if (!parseIataCode(from) || !parseIataCode(to) || !parseIsoDate(date)) {
  throw new Error('invalid route/date arguments; fix before invoking the flight search');
}

Type guard

function isBatchSearchOk(payload) {
  return !!payload && typeof payload === 'object' && payload.status === 0;
}

Try / catch

try {
  const rows = await cli.run(['ctrip', 'flight', from, to, date]);
} catch (err) {
  const m = String(err.message).match(/Ctrip flight API failed \(status=(\S+)\): (.*)/);
  if (m) console.error(`Ctrip business error ${m[1]}: ${m[2]} — refresh session and retry`);
  else throw err;
}

Prevention

When it happens

Trigger: The browser-captured POST to /international/search/api/search/batchSearch returns HTTP 200 but payload.status !== 0 — e.g. expired/invalid Ctrip session cookies, risk-control rejection, invalid search parameters forwarded by the page, or Ctrip-side throttling/business errors.

Common situations: Developers hit this when reusing a stale browser profile whose Ctrip session expired, when Ctrip's anti-bot risk control flags the automated browser, when querying routes/dates Ctrip considers invalid, or during Ctrip API contract changes that shift the status field semantics.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/4bc04c60baf97ae7. Report an issue: GitHub.