jackwener/OpenCLI · error · ArgumentError

--from and --to must differ (both resolved to ${dest.name})

Error message

--from and --to must differ (both resolved to ${dest.name})

What it means

After both endpoints resolve, the library compares origin.cityId and dest.cityId and rejects searches where they are equal — Trip.com package (flight+hotel) searches require distinct origin and destination cities. The message helpfully shows the shared resolved city name.

Source

Thrown at clis/trip/package.js:70

        const to = parseKeyword('to', kwargs.to);
        const depart = parseIsoDate('depart', kwargs.depart);
        const ret = parseIsoDate('return', kwargs.return);
        if (depart >= ret) {
            throw new ArgumentError(`--depart must be before --return (got ${depart} .. ${ret})`);
        }
        const adults = parseAdults(kwargs.adults);
        const limit = parseListLimit(kwargs.limit);

        const origin = await resolvePackageCity(from);
        if (!origin) {
            throw new ArgumentError(`Could not resolve origin "${from}" to a Trip.com city; run 'trip search ${from}' to find the name`);
        }
        const dest = await resolvePackageCity(to);
        if (!dest) {
            throw new ArgumentError(`Could not resolve destination "${to}" to a Trip.com city; run 'trip search ${to}' to find the name`);
        }
        if (origin.cityId === dest.cityId) {
            throw new ArgumentError(`--from and --to must differ (both resolved to ${dest.name})`);
        }

        const groups = await fetchPackageSearch({
            dcode: origin.cityCode,
            acode: dest.cityCode,
            hcityid: String(dest.cityId),
            depart,
            ret,
            adults,
        });
        if (groups.length === 0) {
            throw new EmptyResultError('trip package', `No flight+hotel packages for ${origin.name} to ${dest.name} on ${depart} .. ${ret}`);
        }
        const rows = groups
            .filter((g) => g && Array.isArray(g.flightlist) && g.flightlist.length)
            .map((g) => mapPackageRow(g, 0))
            .filter((row) => row.flightNo && row.from && row.to && row.departure && row.arrival);
        if (rows.length === 0) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Choose genuinely different origin and destination cities
  2. Compare resolved city ids in your wrapper before invoking the CLI
  3. Catch ArgumentError and show the user the merged city name so they can correct one endpoint
  4. Note that distinct names are not enough — the underlying cityIds must differ

Example fix

// before
--from "NYC" --to "New York" // both resolve to same cityId
// after
--from "New York" --to "Boston"
Defensive patterns

Strategy: validation

Validate before calling

const origin = await resolvePackageCity(from);
const dest = await resolvePackageCity(to);
if (origin && dest && origin.cityId === dest.cityId) {
  throw new Error(`--from and --to both resolve to ${dest.name}; pick distinct cities`);
}

Try / catch

try {
  await tripPackageSearch({ from, to });
} catch (e) {
  if (e instanceof ArgumentError && e.message.includes('must differ')) {
    throw new UserInputError(`Both endpoints resolved to ${dest.name}; choose a different origin or destination`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing --from and --to that normalize to the same Trip.com city, e.g. spelling variants ('NYC' and 'New York'), an alias and the canonical name, or genuinely identical values.

Common situations: Users specifying a metro area via two aliases, scripts echoing the same variable into both flags, or assuming nearby-but-distinct suburbs would resolve differently when both map to one cityId.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


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