{"record":{"id":"29f11e940f5972df","repo":"jackwener/OpenCLI","slug":"to-station-must-not-be-empty-29f11e","errorCode":null,"errorMessage":"<to> station must not be empty","messagePattern":"<to> station must not be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/12306/trains.js","lineNumber":126,"sourceCode":"    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\n        if (decoded.length === 0) {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/12306/trains.js#L108-L144","documentation":"ArgumentError thrown by the 12306 trains command when the positional `to` argument is missing, null, or whitespace-only after trimming. The destination station is required to form the leftTicket query, so it is validated immediately after `from` and before any network requests.","triggerScenarios":"Calling the trains command with `to` undefined, empty string, or whitespace only — e.g. `trains 北京 \"\" 2026-09-01`, or programmatic invocation with `kwargs.to` null.","commonSituations":"Forgot to fill the destination placeholder in a copied command; destination variable unset in a script; positional argument reordered so the destination lands in the date slot; piping output where the second field is blank.","solutions":["Provide a non-empty destination station: Chinese name (上海虹桥), telecode (AOH), or pinyin (shanghaihongqiao).","Check argument order — from, to, then date; a missing positional shifts everything left.","In scripts, validate the destination variable before invoking.","Quote station names that may contain spaces."],"exampleFix":"// before\nconst args = [origin, dest, date]; // dest may be ''\nawait trains(...args);\n// after\nif (!dest?.trim()) throw new Error('destination station required');\nconst args = [origin, dest, date];\nawait trains(...args);","handlingStrategy":"validation","validationCode":"if (!to || !String(to).trim()) {\n  throw new Error('destination 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' && /<to> station must not be empty/.test(e.message)) {\n    console.error('Destination missing. Usage: trains <from> <to> <YYYY-MM-DD>');\n  } else throw e;\n}","preventionTips":["Always supply both positional stations; verify with --help.","Validate batch inputs for blank destination columns.","Quote station names containing spaces.","Check argument order — a missing positional shifts the rest."],"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"}