{"record":{"id":"5358fbc31ae4538a","repo":"jackwener/OpenCLI","slug":"from-station-must-not-be-empty-5358fb","errorCode":null,"errorMessage":"<from> station must not be empty","messagePattern":"<from> station must not be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/12306/trains.js","lineNumber":125,"sourceCode":"    description: 'List trains between two 12306 stations on a given date (anonymous, no login required)',\n    domain: 'kyfw.12306.cn',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'from', positional: true, required: true, help: 'Origin station: Chinese name (北京), telecode (BJP), or pinyin (beijing)' },\n        { name: 'to', positional: true, required: true, help: 'Destination station: same forms as <from>' },\n        { name: 'date', required: true, help: 'Departure date in YYYY-MM-DD' },\n        { name: 'limit', type: 'int', default: 50, help: `Maximum rows (1-${MAX_LIMIT})` },\n    ],\n    columns: [\n        'code', 'from_station', 'to_station', 'start_time', 'arrive_time',\n        'duration', 'available', 'business_seat', 'first_seat', 'second_seat',\n        'soft_sleeper', 'hard_sleeper', 'hard_seat', 'no_seat', 'train_no',\n    ],\n    func: async (kwargs) => {\n        const fromArg = String(kwargs.from ?? '').trim();\n        const toArg = String(kwargs.to ?? '').trim();\n        if (!fromArg) throw new ArgumentError('<from> station must not be empty');\n        if (!toArg) throw new ArgumentError('<to> station must not be empty');\n        const date = validateDate(kwargs.date);\n        const limit = normalizeLimit(kwargs.limit, 50, MAX_LIMIT);\n\n        const stations = await fetchStationBundle();\n        const fromStation = resolveStation(stations, fromArg);\n        const toStation = resolveStation(stations, toArg);\n        if (fromStation.code === toStation.code) {\n            throw new ArgumentError(`<from> and <to> must differ; both resolved to ${fromStation.name} (${fromStation.code})`);\n        }\n        const stationByCode = new Map(stations.map((s) => [s.code, s]));\n\n        const cookieHeader = await mintSession();\n        const rawRows = await queryLeftTickets(cookieHeader, fromStation.code, toStation.code, date);\n        const decoded = rawRows\n            .map((line) => parseTrainRecord(decodeURIComponent(line.replace(/%0A/g, '')), stationByCode))\n            .filter(Boolean);\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/12306/trains.js#L107-L143","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait trains({ from: process.env.ORIGIN, to: '上海', date: '2026-09-01' });\n// after\nif (!process.env.ORIGIN?.trim()) throw new Error('ORIGIN env var must be set');\nawait trains({ from: process.env.ORIGIN, to: '上海', date: '2026-09-01' });","handlingStrategy":"validation","validationCode":"if (!from || !String(from).trim()) {\n  throw new Error('origin station is required: Chinese name, telecode, or pinyin');\n}","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  await trains({ from, to, date });\n} catch (e) {\n  if (e.name === 'ArgumentError' && /<from> station must not be empty/.test(e.message)) {\n    console.error('Usage: trains <from> <to> <YYYY-MM-DD>');\n  } else throw e;\n}","preventionTips":["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."],"tags":["argument-validation","missing-argument","cli"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}