jackwener/OpenCLI · error · ArgumentError
--from and --to must differ; both resolved to ${fromStation.
Error message
--from and --to must differ; both resolved to ${fromStation.name} (${fromStation.code}) What it means
After resolving both --from and --to through the station bundle, the library throws this ArgumentError when both arguments resolve to the same station telecode — a price query for an identical origin/destination is meaningless and 12306 would reject it. The message includes the resolved station name and code to reveal which input matched (e.g. aliases of the same station).
Source
Thrown at clis/12306/price.js:148
`<train-no> "${trainNo}" does not look like a 12306 internal train_no`,
'Use the train_no field from `12306 trains` output (e.g. 24000000G10L), not the public code (G1).',
);
}
const fromArg = String(kwargs.from ?? '').trim();
const toArg = String(kwargs.to ?? '').trim();
if (!fromArg) throw new ArgumentError('--from station must not be empty');
if (!toArg) throw new ArgumentError('--to station must not be empty');
const date = validateDate(kwargs.date);
const seatTypes = String(kwargs['seat-types'] ?? '').trim() || 'OM9PA1A3A4FWZ';
if (!SEAT_TYPES_RE.test(seatTypes)) {
throw new ArgumentError('--seat-types must contain only 12306 seat letters/digits (A-Z, 0-9)');
}
const stations = await fetchStationBundle();
const fromStation = resolveStation(stations, fromArg);
const toStation = resolveStation(stations, toArg);
if (fromStation.code === toStation.code) {
throw new ArgumentError(`--from and --to must differ; both resolved to ${fromStation.name} (${fromStation.code})`);
}
const cookieHeader = await mintSession();
const stops = await queryStopsForPrice(cookieHeader, trainNo, fromStation.code, toStation.code, date);
const { fromNo, toNo } = pickStationNos(stops, fromStation.code, toStation.code, fromStation.name, toStation.name);
const priceData = await queryPrice(cookieHeader, trainNo, fromNo, toNo, seatTypes, date);
const rows = parsePriceData(priceData);
if (rows.length === 0) {
throw new EmptyResultError(
`No prices returned for train_no=${trainNo} ${fromStation.name} -> ${toStation.name} on ${date}`,
'Try a different seat-types letter set, or check that this train operates on the date.',
);
}
return rows;
},
});
export const __test__ = { parsePriceData, pickStationNos, queryStopsForPrice, queryPrice, SEAT_LETTERS, TRAIN_NO_RE };View on GitHub (pinned to 49907e53dc)
Solutions
- Choose genuinely different origin and destination stations for the segment.
- Check the resolved name/code in the message; if an alias collided, use the more specific station name or telecode.
- Validate from !== to (after resolution) in scripts before invoking the command.
Example fix
// before 12306 price --train-no 24000000G10L --from 上海 --to 上海 // after 12306 price --train-no 24000000G10L --from 上海 --to 南京
Defensive patterns
Strategy: validation
Validate before calling
if (String(opts.from).trim() === String(opts.to).trim()) {
throw new Error('--from and --to must be different stations');
} Try / catch
try {
await price({ 'train-no': tn, from, to, date });
} catch (e) {
if (/both resolved to/.test(e.message)) {
const [name, code] = e.message.match(/resolved to (.+?) \((.+?)\)/)?.slice(1) ?? [];
console.error(`${from} and ${to} both resolve to ${name} (${code}); pick a different endpoint.`);
} else throw e;
} Prevention
- Never pass the same station (or two aliases of it) as --from and --to.
- Resolve both arguments via `12306 stations` first and compare the returned `code` fields.
- When templating, assert the two station variables differ before invocation.
When it happens
Trigger: Passing the same station twice (`--from 上海 --to 上海`), or two aliases of one station (e.g. `--from 北京 --to 北京南` when both resolve to the same telecode, or pinyin vs Chinese name of the same station).
Common situations: Template variables both defaulting to the same city; users not realizing pinyin/telecode/Chinese name forms can resolve to the same station; copy-paste duplication in generated commands.
Related errors
- <train-no> "${trainNo}" does not look like a 12306 internal
- --from station must not be empty
- --to station must not be empty
- --seat-types must contain only 12306 seat letters/digits (A-
- keyword must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/90c6ae6092d0b57a.
Report an issue: GitHub.