{"record":{"id":"b08b87e91e61cbf5","repo":"jackwener/OpenCLI","slug":"from-station-must-not-be-empty","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/price.js","lineNumber":136,"sourceCode":"        { name: 'train-no', positional: true, required: true, help: 'Internal train_no from `12306 trains` (e.g. 24000000G10L)' },\n        { name: 'from', required: true, help: 'Origin station (Chinese name, telecode, or pinyin) - must be a stop of this train' },\n        { name: 'to', required: true, help: 'Destination station - must be a stop of this train' },\n        { name: 'date', required: true, help: 'Departure date in YYYY-MM-DD' },\n        { name: 'seat-types', default: 'OM9PA1A3A4FWZ', help: 'Seat-type letters to query (default covers the common classes). Examples: OM9 (二等/一等/商务), A1A3A4 (硬座/硬卧/软卧).' },\n    ],\n    columns: ['seat_code', 'seat_name', 'price', 'currency'],\n    func: async (kwargs) => {\n        const trainNo = String(kwargs['train-no'] ?? '').trim();\n        if (!trainNo) throw new ArgumentError('<train-no> must not be empty');\n        if (!TRAIN_NO_RE.test(trainNo)) {\n            throw new ArgumentError(\n                `<train-no> \"${trainNo}\" does not look like a 12306 internal train_no`,\n                'Use the train_no field from `12306 trains` output (e.g. 24000000G10L), not the public code (G1).',\n            );\n        }\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 seatTypes = String(kwargs['seat-types'] ?? '').trim() || 'OM9PA1A3A4FWZ';\n        if (!SEAT_TYPES_RE.test(seatTypes)) {\n            throw new ArgumentError('--seat-types must contain only 12306 seat letters/digits (A-Z, 0-9)');\n        }\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\n        const cookieHeader = await mintSession();\n        const stops = await queryStopsForPrice(cookieHeader, trainNo, fromStation.code, toStation.code, date);\n        const { fromNo, toNo } = pickStationNos(stops, fromStation.code, toStation.code, fromStation.name, toStation.name);\n        const priceData = await queryPrice(cookieHeader, trainNo, fromNo, toNo, seatTypes, date);","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/12306/price.js#L118-L154","documentation":"The `12306 price` command requires both origin and destination. This ArgumentError is thrown when the `--from` option is missing or empty after trimming, before any network request is made.","triggerScenarios":"Calling `12306 price --train-no <tn> --to 上海` without `--from`, or with `--from \"\"` / whitespace-only value.","commonSituations":"Forgotten option in shell scripts; variables that expand to empty string (unset env var or empty JSON field); wrong option name (e.g. `--station-from`) so `from` is undefined.","solutions":["Pass `--from <station>` with a station name, telecode, or pinyin, e.g. `--from 北京南`.","Check shell variables: ensure the value interpolating into --from is non-empty (`echo \"$FROM\"`).","Verify the option spelling matches `--from` exactly."],"exampleFix":"// before (FROM unset)\n12306 price --train-no 24000000G10L --from \"$FROM\" --to 上海\n// after\nFROM=北京南; [ -n \"$FROM\" ] && 12306 price --train-no 24000000G10L --from \"$FROM\" --to 上海","handlingStrategy":"validation","validationCode":"const from = String(opts.from ?? '').trim();\nif (!from) throw new Error('--from station is required, e.g. --from 北京南');","typeGuard":null,"tryCatchPattern":"try {\n  await price({ 'train-no': tn, from, to, date });\n} catch (e) {\n  if (e instanceof ArgumentError && /--from station must not be empty/.test(e.message)) {\n    console.error('Supply --from with a station name, telecode, or pinyin.');\n  } else throw e;\n}","preventionTips":["Check shell variables are non-empty before interpolation: [ -n \"$FROM\" ] || exit 1.","Use exact option name --from.","Always pass both --from and --to together as a pair."],"tags":["argument-validation","cli","missing-option"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}