jackwener/OpenCLI · error · ArgumentError
--from and --to must differ (got ${fromName})
Error message
--from and --to must differ (got ${fromName}) What it means
Thrown by the ctrip train list command when the --from and --to values resolve to the same place name. The scraper needs an origin/destination pair to build a Ctrip train search URL; identical endpoints produce an invalid search. It is an ArgumentError because it is a caller input mistake, not a runtime failure.
Source
Thrown at clis/ctrip/train.js:48
args: [
{ name: 'from', required: true, positional: true, help: 'Departure station or city name (e.g. 北京 / 上海虹桥)' },
{ name: 'to', required: true, positional: true, help: 'Arrival station or city name (e.g. 上海 / 杭州东)' },
{ name: 'date', required: true, help: 'Departure date (YYYY-MM-DD)' },
{ name: 'limit', default: 20, help: 'Number of trains (1-50)' },
],
columns: [
'rank',
'trainNo',
'departureTime', 'departureStation',
'arrivalTime', 'arrivalStation',
'duration', 'fromPrice', 'seats',
'url',
],
func: async (page, kwargs) => {
const fromName = parsePlaceName('from', kwargs.from);
const toName = parsePlaceName('to', kwargs.to);
if (fromName === toName) {
throw new ArgumentError(`--from and --to must differ (got ${fromName})`);
}
const date = parseIsoDate('date', kwargs.date);
const limit = parseListLimit(kwargs.limit);
const searchUrl = buildTrainListUrl(fromName, toName, date);
await page.goto(searchUrl);
const waitResult = await page.evaluate(WAIT_FOR_TRAINS_JS);
if (waitResult === 'captcha') {
throw new AuthRequiredError('trains.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');
}
if (waitResult !== 'content') {
throw new CommandExecutionError(`Ctrip train page did not render train cards (state=${String(waitResult)})`);
}
const renderedCardCount = await page.evaluate(buildScrollUntilJs('.card-white.list-item', limit));
const raw = await page.evaluate(buildTrainExtractJs());
if (!Array.isArray(raw)) {
throw new CommandExecutionError('Ctrip train DOM extraction returned malformed rows');
}View on GitHub (pinned to 49907e53dc)
Solutions
- Pass distinct origin and destination values for --from and --to.
- Check how parsePlaceName normalizes input (trims, case, aliases) to understand why two different-looking strings collided.
- Add a pre-flight check in your script comparing normalized from/to before invoking the command.
Example fix
// before
await runTrainList({ from: '上海', to: '上海', date: '2026-09-01' });
// after
await runTrainList({ from: '上海', to: '北京', date: '2026-09-01' }); Defensive patterns
Strategy: validation
Validate before calling
function norm(s){return String(s).trim().toLowerCase();}
if (norm(opts.from) === norm(opts.to)) throw new Error('--from and --to must differ'); Type guard
const isDistinctPair = (from, to) => norm(from) !== norm(to);
Try / catch
try { await runTrainList(opts); } catch (e) { if (e instanceof ArgumentError && /must differ/.test(e.message)) { console.error('Fix from/to flags'); } else throw e; } Prevention
- Always pass distinct origin/destination values
- Compare normalized from/to in wrapper scripts before invoking
- Check parsePlaceName normalization rules when using aliases
When it happens
Trigger: Calling the train list command with --from and --to that normalize to the same string via parsePlaceName, e.g. --from '上海' --to '上海' or --from 'Shanghai' --to 'shanghai'.
Common situations: Copy-pasting the same station into both flags; scripting a loop where from/to variables are accidentally assigned the same value; case or whitespace differences that resolve to the same canonical name.
Related errors
- tid must be a numeric thread id
- --from and --to must differ (got ${fromCity})
- --from and --to must differ (got ${fromCode})
- Unknown --since "${sinceKey}". Valid: ${Object.keys(SINCE).j
- --limit must be a positive integer
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/bc9f264345feed8f.
Report an issue: GitHub.