jackwener/OpenCLI · warning · ArgumentError

--from and --to must differ (got ${fromCity})

Error message

--from and --to must differ (got ${fromCity})

What it means

ArgumentError thrown before any navigation when the parsed departure and arrival city names are identical for the `ctrip ferry` command. A ferry route with the same origin and destination is meaningless and would produce an invalid ship.ctrip.com deep link, so the CLI validates up front via parsePlaceName and rejects it with the offending city name in the message. Pure input-validation error — no network or browser involved.

Source

Thrown at clis/ctrip/ferry.js:49

    args: [
        { name: 'from', required: true, positional: true, help: 'Departure city name (e.g. 大连 / 海口)' },
        { name: 'to', required: true, positional: true, help: 'Arrival city name (e.g. 烟台 / 海安)' },
        { name: 'date', required: true, help: 'Departure date (YYYY-MM-DD)' },
        { name: 'limit', default: 20, help: 'Number of sailings (1-50)' },
    ],
    columns: [
        'rank',
        'shipName',
        'departureTime', 'fromPort',
        'arrivalTime', 'toPort',
        'duration', 'price', 'status',
        'url',
    ],
    func: async (page, kwargs) => {
        const fromCity = parsePlaceName('from', kwargs.from);
        const toCity = parsePlaceName('to', kwargs.to);
        if (fromCity === toCity) {
            throw new ArgumentError(`--from and --to must differ (got ${fromCity})`);
        }
        const date = parseIsoDate('date', kwargs.date);
        const limit = parseListLimit(kwargs.limit);

        const searchUrl = buildFerryListUrl(fromCity, toCity, date);
        await page.goto(searchUrl);
        const waitResult = await page.evaluate(WAIT_FOR_FERRY_JS);
        if (waitResult === 'captcha') {
            throw new AuthRequiredError('ship.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
        }
        if (waitResult !== 'content') {
            throw new CommandExecutionError(`Ctrip ferry page did not render sailing rows (state=${String(waitResult)})`);
        }
        const renderedCardCount = await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));
        const raw = await page.evaluate(buildFerryExtractJs());
        if (!Array.isArray(raw)) {
            throw new CommandExecutionError('Ctrip ferry DOM extraction returned malformed rows');
        }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass distinct --from and --to cities (e.g. 大连 to 烟台)
  2. Fix the calling script so from/to are not assigned the same value
  3. Normalize/compare route pairs in your own code before invoking to filter out degenerate routes

Example fix

// before
await run(['ctrip', 'ferry', '--from', city, '--to', city, '--date', date]);
// after
if (city !== dest) {
  await run(['ctrip', 'ferry', '--from', city, '--to', dest, '--date', date]);
}
Defensive patterns

Strategy: validation

Validate before calling

function validateFerryArgs(from, to) {
  const norm = s => String(s || '').trim();
  if (!norm(from)) throw new Error('--from is required');
  if (!norm(to)) throw new Error('--to is required');
  if (norm(from) === norm(to)) throw new Error(`--from and --to must differ (got ${norm(from)})`);
}
validateFerryArgs(kwargs.from, kwargs.to);

Try / catch

try {
  return await run(['ctrip', 'ferry', '--from', f, '--to', t, '--date', d]);
} catch (e) {
  if (e instanceof Error && e.name === 'ArgumentError') {
    console.error('Bad route arguments:', e.message);
    process.exitCode = 2; // usage error, not a runtime failure
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `ctrip ferry --from 大连 --to 大连 ...` or any invocation where the normalized from/to place names (after parsePlaceName) compare equal, including case/whitespace-equivalent inputs that normalize to the same city.

Common situations: Copy-pasting the same city into both flags; scripting where from/to variables are accidentally assigned the same value; typo in a loop over route pairs; assuming the CLI would silently return empty results instead of validating.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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