jackwener/OpenCLI · error · ArgumentError
<from> station must not be empty
Error message
<from> station must not be empty
What it means
ArgumentError thrown by the 12306 trains command when the positional `from` argument is missing, null, or whitespace-only after trimming. The command requires an origin station to build the leftTicket query, so it validates both station arguments up front before any network calls.
Source
Thrown at clis/12306/trains.js:125
description: 'List trains between two 12306 stations on a given date (anonymous, no login required)',
domain: 'kyfw.12306.cn',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'from', positional: true, required: true, help: 'Origin station: Chinese name (北京), telecode (BJP), or pinyin (beijing)' },
{ name: 'to', positional: true, required: true, help: 'Destination station: same forms as <from>' },
{ name: 'date', required: true, help: 'Departure date in YYYY-MM-DD' },
{ name: 'limit', type: 'int', default: 50, help: `Maximum rows (1-${MAX_LIMIT})` },
],
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);
View on GitHub (pinned to 49907e53dc)
Solutions
- Pass a non-empty origin station: Chinese name (北京), telecode (VAP), or pinyin (beijingbei).
- Check shell quoting/expansion — echo the command with arguments filled before running.
- In scripts, assert the origin variable is non-empty before invoking the command.
- If the station name may contain spaces, quote it.
Example fix
// before
await trains({ from: process.env.ORIGIN, to: '上海', date: '2026-09-01' });
// after
if (!process.env.ORIGIN?.trim()) throw new Error('ORIGIN env var must be set');
await trains({ from: process.env.ORIGIN, to: '上海', date: '2026-09-01' }); Defensive patterns
Strategy: validation
Validate before calling
if (!from || !String(from).trim()) {
throw new Error('origin station is required: Chinese name, telecode, or pinyin');
} Type guard
function isNonEmptyString(v) {
return typeof v === 'string' && v.trim().length > 0;
} Try / catch
try {
await trains({ from, to, date });
} catch (e) {
if (e.name === 'ArgumentError' && /<from> station must not be empty/.test(e.message)) {
console.error('Usage: trains <from> <to> <YYYY-MM-DD>');
} else throw e;
} Prevention
- Validate CLI variables are set/non-empty before invoking.
- Quote station arguments in shell to avoid them being dropped.
- Check positional argument order (from, to, date).
- Use `set -u` / strict modes in shell scripts to catch unset variables.
When it happens
Trigger: Calling the trains command/function with `from` undefined, an empty string, or a string of only spaces — e.g. `trains "" "上海" 2026-09-01` or programmatically passing `kwargs.from` as null/undefined.
Common situations: Shell quoting mistake that drops the first argument; scripting the command with an unexpanded variable (`$ORIGIN` empty); building kwargs programmatically and leaving the from field unset; copying a command template without filling in the origin.
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
- keyword must not be empty
- <to> station must not be empty
- who 不能为空
- key is required
- ${label} is required (YYYY-MM-DD)
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5358fbc31ae4538a.
Report an issue: GitHub.