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
- Pass distinct --from and --to cities (e.g. 大连 to 烟台)
- Fix the calling script so from/to are not assigned the same value
- 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
- Validate from !== to in your own script before shelling out to the CLI
- Normalize place names (trim/case) before comparing or passing
- Guard scripted route lists so the same city is never assigned to both ends
- Surface ArgumentError as a usage error (exit code 2), distinct from network/data errors
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
- coingecko derivatives limit must be a positive integer
- ${label} must be a positive integer
- ${label} must be an integer >= ${min}
- Invalid HN item id: ${args.id}
- thread-or-recipient is required
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/381fef90e214038d.
Report an issue: GitHub.