jackwener/OpenCLI · error · ArgumentError
--seat-types must contain only 12306 seat letters/digits (A-
Error message
--seat-types must contain only 12306 seat letters/digits (A-Z, 0-9)
What it means
The `--seat-types` option must be a string of 1-32 uppercase letters/digits matching SEAT_TYPES_RE (/^[A-Z0-9]{1,32}$/), because 12306's price endpoint keys prices by seat-type letter codes (O=二等座, M=一等座, A9=商务座, WZ=无座, etc.). The library throws this ArgumentError when the supplied value contains lowercase letters or other characters. Empty values are fine — they default to 'OM9PA1A4FWZ' style defaults.
Source
Thrown at clis/12306/price.js:141
],
columns: ['seat_code', 'seat_name', 'price', 'currency'],
func: async (kwargs) => {
const trainNo = String(kwargs['train-no'] ?? '').trim();
if (!trainNo) throw new ArgumentError('<train-no> must not be empty');
if (!TRAIN_NO_RE.test(trainNo)) {
throw new ArgumentError(
`<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.',View on GitHub (pinned to 49907e53dc)
Solutions
- Omit --seat-types entirely to use the default set 'OM9PA1A3A4FWZ'.
- Use uppercase letter codes only: e.g. --seat-types OM9 (O=二等座, M=一等座, 9/A9=商务座).
- Uppercase and strip separators before passing: value.toUpperCase().replace(/[^A-Z0-9]/g, '').
Example fix
// before 12306 price --train-no 24000000G10L --from 北京南 --to 上海 --seat-types "o, m" // after 12306 price --train-no 24000000G10L --from 北京南 --to 上海 --seat-types OM # O=二等座 M=一等座
Defensive patterns
Strategy: validation
Validate before calling
const seatTypes = String(opts['seat-types'] ?? '').trim().toUpperCase().replace(/[^A-Z0-9]/g, '');
if (seatTypes && !/^[A-Z0-9]{1,32}$/.test(seatTypes)) {
throw new Error('--seat-types must be uppercase letter codes only (e.g. OM9), or omitted for the default');
} Type guard
const isValidSeatTypes = (v) => typeof v === 'string' && /^[A-Z0-9]{1,32}$/.test(v); Try / catch
try {
await price({ 'train-no': tn, from, to, date, 'seat-types': st });
} catch (e) {
if (/--seat-types must contain only/.test(e.message)) {
// drop the option and use the library default
await price({ 'train-no': tn, from, to, date });
} else throw e;
} Prevention
- Omit --seat-types unless you specifically need a subset of seat classes.
- Use uppercase letter codes (O, M, A9, A1, A3, A4, F, P, WZ), never seat names or lowercase.
- Sanitize by uppercasing and stripping non-alphanumerics before passing.
When it happens
Trigger: Passing `--seat-types "o,m"` (lowercase/commas), `--seat-types 二等座` (Chinese text), or any value with symbols/spaces to `12306 price`.
Common situations: Users pass human-readable seat names instead of letter codes; lowercase seat letters copied from other tools; commas or spaces separating multiple seat types.
Related errors
- <train-no> "${trainNo}" does not look like a 12306 internal
- coords must be in "x,y" format (e.g. "1,2")
- semanticscholar paper id "${value}" is not recognised
- --${name} must be a numeric Trip.com hotel id, got ${JSON.st
- --from station must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/f7e6b9020d10c9c3.
Report an issue: GitHub.