jackwener/OpenCLI · error · ArgumentError
<train-no> must not be empty
Error message
<train-no> must not be empty
What it means
The `12306 price` command throws this ArgumentError during argument validation when the positional <train-no> argument is missing or resolves to an empty/whitespace-only string. It fails fast before any network calls because the internal train_no is required for every downstream API call.
Source
Thrown at clis/12306/price.js:127
cli({
site: '12306',
name: 'price',
access: 'read',
description: 'Look up 12306 ticket prices by seat class for one train on a given date and segment (anonymous, no login required)',
domain: 'kyfw.12306.cn',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'train-no', positional: true, required: true, help: 'Internal train_no from `12306 trains` (e.g. 24000000G10L)' },
{ name: 'from', required: true, help: 'Origin station (Chinese name, telecode, or pinyin) - must be a stop of this train' },
{ name: 'to', required: true, help: 'Destination station - must be a stop of this train' },
{ name: 'date', required: true, help: 'Departure date in YYYY-MM-DD' },
{ name: 'seat-types', default: 'OM9PA1A3A4FWZ', help: 'Seat-type letters to query (default covers the common classes). Examples: OM9 (二等/一等/商务), A1A3A4 (硬座/硬卧/软卧).' },
],
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);View on GitHub (pinned to 49907e53dc)
Solutions
- Pass the internal train_no positional argument, e.g. `12306 price 24000000G10L --from 北京南 --to 上海虹桥 --date 2026-09-01`.
- Get the value from the train_no column of `12306 trains` output, not the public code (G1).
- Check that the variable feeding the argument is non-empty in your script (guard before invoking).
- Ensure proper quoting so the shell does not drop the token.
- Distinguish this empty-argument error from the companion format error (non-empty but not matching TRAIN_NO_RE) — fix whichever validation you hit.
Example fix
// before
const trainNo = rows[0].public_code; // e.g. "G1", or empty if row missing
await priceCmd({ 'train-no': trainNo, from: '北京南', to: '上海虹桥', date });
// after
const trainNo = rows[0]?.train_no ?? '';
if (!trainNo.trim()) throw new Error('run `12306 trains` first; train_no is required');
await priceCmd({ 'train-no': trainNo, from: '北京南', to: '上海虹桥', date }); Defensive patterns
Strategy: validation
Validate before calling
const trainNo = String(kwargs['train-no'] ?? '').trim();
if (!trainNo) throw new Error('12306 price requires a positional <train-no>; get it from `12306 trains` (train_no column)');
if (!/^[0-9A-Za-z]{8,18}$/.test(trainNo)) throw new Error("'" + trainNo + "' is not an internal train_no (public codes like G1 are rejected)"); Type guard
function isValidTrainNo(v) {
return typeof v === 'string' && /^[0-9A-Za-z]{8,18}$/.test(v.trim());
} Try / catch
try {
rows = await run12306Price({ 'train-no': trainNo, from, to, date });
} catch (err) {
if (err instanceof ArgumentError && /must not be empty/.test(err.message)) {
console.error('Usage: 12306 price <train-no> --from <station> --to <station> --date YYYY-MM-DD');
process.exitCode = 2;
return;
}
throw err;
} Prevention
- Always pass train_no positionally: `12306 price 24000000G10L --from ... --to ... --date ...`.
- Source train_no from the `12306 trains` output's train_no column, not the public code.
- Guard upstream variables in scripts so an empty lookup result cannot silently drop the argument.
- Quote shell arguments to prevent tokens being swallowed by expansion.
When it happens
Trigger: Calling `12306 price` with no positional argument (`12306 price --from X --to Y --date D`), passing an empty string, or a value that is only whitespace; programmatically passing undefined/null kwargs['train-no'].
Common situations: Script-built command lines where a variable is empty because an upstream `12306 trains` lookup failed; shell quoting mistakes dropping the argument; copying only the public code after deleting the train_no field; template expansion leaving the positional blank.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Pass exactly one of --url <url>, --content <text>, or --file
- keyword must not be empty
- <from> station must not be empty
- <to> station must not be empty
- who 不能为空
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6d7ea33e5ec29028.
Report an issue: GitHub.