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

  1. Choose genuinely different origin and destination stations for the segment.
  2. Check the resolved name/code in the message; if an alias collided, use the more specific station name or telecode.
  3. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/90c6ae6092d0b57a. Report an issue: GitHub.