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
ArgumentError thrown when `from` and `to` resolve to the same station telecode. 12306 cannot answer a leftTicket query whose origin equals the destination (there are no trains from a station to itself), so the command rejects it after resolution, reporting the resolved station name and telecode.
Source
Thrown at clis/12306/trains.js:134
],
columns: [
'code', 'from_station', 'to_station', 'start_time', 'arrive_time',
'duration', 'available', 'business_seat', 'first_seat', 'second_seat',
'soft_sleeper', 'hard_sleeper', 'hard_seat', 'no_seat', 'train_no',
],
func: async (kwargs) => {
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 limit = normalizeLimit(kwargs.limit, 50, MAX_LIMIT);
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 stationByCode = new Map(stations.map((s) => [s.code, s]));
const cookieHeader = await mintSession();
const rawRows = await queryLeftTickets(cookieHeader, fromStation.code, toStation.code, date);
const decoded = rawRows
.map((line) => parseTrainRecord(decodeURIComponent(line.replace(/%0A/g, '')), stationByCode))
.filter(Boolean);
if (decoded.length === 0) {
throw new EmptyResultError(
`No trains found from ${fromStation.name} to ${toStation.name} on ${date}`,
'Try a different date or check whether the route is operated by 12306.',
);
}
return decoded.slice(0, limit);
},
});View on GitHub (pinned to 49907e53dc)
Solutions
- Supply genuinely different origin and destination stations.
- If you intended a different station in the same city, use the specific station name (e.g. 北京南 instead of 北京).
- Check resolved stations before calling: run the stations command or resolveStation to confirm the two inputs map to different telecodes.
- Fix scripts where from/to come from the same config variable.
Example fix
// before
await trains({ from: '北京', to: 'beijing', date: '2026-09-01' }); // same station
// after
await trains({ from: '北京', to: '上海虹桥', date: '2026-09-01' }); Defensive patterns
Strategy: validation
Validate before calling
// Resolve first, compare telecodes, then call:
const stations = await fetchStationBundle();
const a = resolveStation(stations, from);
const b = resolveStation(stations, to);
if (a.code === b.code) throw new Error(`from/to both resolve to ${a.name} (${a.code})`); Type guard
function areDistinctStations(x, y) {
return x.code !== y.code;
} Try / catch
try {
await trains({ from, to, date });
} catch (e) {
if (e.name === 'ArgumentError' && /must differ; both resolved to/.test(e.message)) {
console.error('Origin and destination are the same station — pick two different stations.');
} else throw e;
} Prevention
- Resolve both inputs to telecodes and compare before querying.
- Remember Chinese name, pinyin, and aliases of one station all collide — don't mix forms of the same station.
- For same-city trips use specific different stations (e.g. 北京南 → 北京丰台).
- In configs, store distinct origin/destination variables and assert inequality.
When it happens
Trigger: Passing two identifiers that resolve to one station — e.g. `trains 北京 beijing 2026-09-01` (Chinese name vs pinyin of the same station), `trains BJP BJP ...`, or two different aliases/telecodes that map to the same physical station via the station bundle.
Common situations: Testing the CLI with the same city twice; confusion between city name and station name when both resolve to the same main station (e.g. 上海 and 上海虹桥 resolving distinctly, but 北京 and 北京南 do not — while 北京 北京南 actually differ, alias collisions like `bjp` and `北京` do not); a script defaulting both endpoints to the same variable.
Related errors
- --from and --to must differ; both resolved to ${fromStation.
- station must not be empty
- <train-no> must not be empty
- <train-no> "${trainNo}" does not look like a 12306 internal
- --from station must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/32d1b5b3cc381599.
Report an issue: GitHub.