jackwener/OpenCLI · error · ArgumentError
--from and --to must differ (got ${fromCity})
Error message
--from and --to must differ (got ${fromCity}) What it means
The ctrip bus list command requires a distinct origin and destination city. parsePlaceName normalizes both --from and --to, and if they resolve to the same city name, ArgumentError is thrown because Ctrip cannot search same-city bus routes. This is a pre-flight argument validation, thrown before any page navigation.
Source
Thrown at clis/ctrip/bus.js:49
navigateBefore: false,
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 departures (1-50)' },
],
columns: [
'rank',
'departureTime',
'fromStation', 'toStation',
'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 = buildBusListUrl(fromCity, toCity, date);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_BUS_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('bus.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Ctrip bus page did not render schedule rows (state=${String(waitResult)})`);
}
const renderedCardCount = await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));
const raw = await page.evaluate(buildBusExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Ctrip bus DOM extraction returned malformed rows');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Pass different values for --from and --to
- Check how your city names normalize (parsePlaceName) — aliases like 上海市 normalize to the same city as 上海
- If you actually want intra-city travel, use a different command or transport type (bus search is intercity)
Example fix
// before clis ctrip bus --from 上海 --to 上海市 // after clis ctrip bus --from 上海 --to 杭州
Defensive patterns
Strategy: validation
Validate before calling
const norm = (s) => String(s || '').trim();
if (norm(from) === norm(to)) {
throw new Error(`--from and --to must differ (got ${norm(from)})`);
} Type guard
null
Try / catch
try {
await ctripBusList({ from, to, date });
} catch (e) {
if (e instanceof ArgumentError && e.message.includes('must differ')) {
console.error('Invalid route: origin and destination are the same city');
process.exitCode = 2;
return;
}
throw e;
} Prevention
- Validate from/to differ (after normalization) in your wrapper scripts
- Beware city aliases (上海市 vs 上海) that normalize identically
- Never default --to to the same variable used for --from in scripts
- Add a unit test covering same-city rejection in any tooling around this CLI
When it happens
Trigger: Invoking the bus command with `--from` and `--to` set to the same city (or values that normalize to the same name, e.g. "上海" vs "上海市").
Common situations: Scripting the CLI with a variable that accidentally defaults to the origin; typos or full/short city names resolving identically; copy-paste leaving both flags the same.
Related errors
- --from and --to must differ (got ${fromCode})
- tid must be a numeric thread id
- --return (${ret}) must be on or after --depart (${depart})
- --from and --to must differ (got ${fromName})
- Unknown --since "${sinceKey}". Valid: ${Object.keys(SINCE).j
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e5b356077dd883e0.
Report an issue: GitHub.