{"record":{"id":"276d2ee36df13822","repo":"jackwener/OpenCLI","slug":"keyword-must-not-be-empty","errorCode":null,"errorMessage":"keyword must not be empty","messagePattern":"keyword must not be empty","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/12306/stations.js","lineNumber":28,"sourceCode":"\nconst MAX_LIMIT = 50;\n\ncli({\n    site: '12306',\n    name: 'stations',\n    access: 'read',\n    description: 'Search 12306 (China Railway) stations by Chinese name, telecode, or pinyin keyword',\n    domain: 'kyfw.12306.cn',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'keyword', positional: true, required: true, help: 'Chinese substring (上海), telecode (AOH), or pinyin (shanghai)' },\n        { name: 'limit', type: 'int', default: 20, help: `Maximum results (1-${MAX_LIMIT})` },\n    ],\n    columns: ['name', 'code', 'pinyin', 'abbr', 'city'],\n    func: async (kwargs) => {\n        const keyword = String(kwargs.keyword ?? '').trim();\n        if (!keyword) throw new ArgumentError('keyword must not be empty');\n        const limit = normalizeLimit(kwargs.limit, 20, MAX_LIMIT);\n\n        const stations = await fetchStationBundle();\n        const lower = keyword.toLowerCase();\n        const matches = stations.filter((s) =>\n            s.name.includes(keyword)\n            || s.code === keyword.toUpperCase()\n            || s.pinyin.includes(lower)\n            || s.abbr.includes(lower)\n            || s.short.includes(lower)\n            || s.city.includes(keyword),\n        );\n        if (matches.length === 0) {\n            throw new EmptyResultError(`No 12306 stations match \"${keyword}\"`);\n        }\n        return matches.slice(0, limit).map((s) => ({\n            name: s.name,\n            code: s.code,","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/12306/stations.js#L10-L46","documentation":"The `12306 stations` command takes a required positional `keyword` used to search station name (Chinese substring), telecode, pinyin, abbreviation, short form, or city. This ArgumentError is thrown when the keyword is missing or empty after trimming — a client-side guard thrown before any network call.","triggerScenarios":"Running `12306 stations` with no positional argument, `12306 stations \"\"`, or `12306 stations \"   \"`; scripts where the keyword variable is unset/empty.","commonSituations":"Forgetting the positional argument (it's required:true); empty variable interpolation in shell scripts; passing the flag value to the wrong slot so keyword is undefined.","solutions":["Pass a non-empty keyword: `12306 stations 上海`, `12306 stations AOH`, or `12306 stations shanghai`.","Guard the variable before invoking: `[ -n \"$KW\" ] && 12306 stations \"$KW\"`.","Quote the keyword to avoid the shell splitting it into separate arguments."],"exampleFix":"// before\n12306 stations   # missing keyword\n// after\n12306 stations 上海","handlingStrategy":"validation","validationCode":"const keyword = String(process.argv[3] ?? '').trim();\nif (!keyword) { console.error('Usage: 12306 stations <keyword>'); process.exit(2); }","typeGuard":null,"tryCatchPattern":"try {\n  const stations = await stationsCmd({ keyword });\n} catch (e) {\n  if (e instanceof ArgumentError && /keyword must not be empty/.test(e.message)) {\n    console.error('Provide a search keyword: Chinese substring, telecode, or pinyin.');\n  } else throw e;\n}","preventionTips":["Always pass a quoted positional keyword: `12306 stations \"上海\"`.","Guard unset shell variables before invocation.","Remember keyword is positional and required — flags won't satisfy it."],"tags":["argument-validation","cli","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}