{"record":{"id":"e5b356077dd883e0","repo":"jackwener/OpenCLI","slug":"from-and-to-must-differ-got-fromcity","errorCode":null,"errorMessage":"--from and --to must differ (got ${fromCity})","messagePattern":"--from and --to must differ \\(got (.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/bus.js","lineNumber":49,"sourceCode":"    navigateBefore: false,\n    args: [\n        { name: 'from', required: true, positional: true, help: 'Departure city name (e.g. 北京 / 上海)' },\n        { name: 'to', required: true, positional: true, help: 'Arrival city name (e.g. 天津 / 杭州)' },\n        { name: 'date', required: true, help: 'Departure date (YYYY-MM-DD)' },\n        { name: 'limit', default: 20, help: 'Number of departures (1-50)' },\n    ],\n    columns: [\n        'rank',\n        'departureTime',\n        'fromStation', 'toStation',\n        'duration', 'price', 'status',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const fromCity = parsePlaceName('from', kwargs.from);\n        const toCity = parsePlaceName('to', kwargs.to);\n        if (fromCity === toCity) {\n            throw new ArgumentError(`--from and --to must differ (got ${fromCity})`);\n        }\n        const date = parseIsoDate('date', kwargs.date);\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl = buildBusListUrl(fromCity, toCity, date);\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_BUS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('bus.ctrip.com', 'Ctrip is asking for a captcha; complete it in your browser session and retry');\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Ctrip bus page did not render schedule rows (state=${String(waitResult)})`);\n        }\n        const renderedCardCount = await page.evaluate(buildScrollUntilJs('.list-item-parent', limit));\n        const raw = await page.evaluate(buildBusExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip bus DOM extraction returned malformed rows');\n        }","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/bus.js#L31-L67","documentation":"The ctrip bus list command requires a distinct origin and destination city. parsePlaceName normalizes both --from and --to, and if they resolve to the same city name, ArgumentError is thrown because Ctrip cannot search same-city bus routes. This is a pre-flight argument validation, thrown before any page navigation.","triggerScenarios":"Invoking the bus command with `--from` and `--to` set to the same city (or values that normalize to the same name, e.g. \"上海\" vs \"上海市\").","commonSituations":"Scripting the CLI with a variable that accidentally defaults to the origin; typos or full/short city names resolving identically; copy-paste leaving both flags the same.","solutions":["Pass different values for --from and --to","Check how your city names normalize (parsePlaceName) — aliases like 上海市 normalize to the same city as 上海","If you actually want intra-city travel, use a different command or transport type (bus search is intercity)"],"exampleFix":"// before\nclis ctrip bus --from 上海 --to 上海市\n// after\nclis ctrip bus --from 上海 --to 杭州","handlingStrategy":"validation","validationCode":"const norm = (s) => String(s || '').trim();\nif (norm(from) === norm(to)) {\n  throw new Error(`--from and --to must differ (got ${norm(from)})`);\n}","typeGuard":"null","tryCatchPattern":"try {\n  await ctripBusList({ from, to, date });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('must differ')) {\n    console.error('Invalid route: origin and destination are the same city');\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Validate from/to differ (after normalization) in your wrapper scripts","Beware city aliases (上海市 vs 上海) that normalize identically","Never default --to to the same variable used for --from in scripts","Add a unit test covering same-city rejection in any tooling around this CLI"],"tags":["argument-validation","cli","ctrip","input-error"],"backgroundTag":"invalid-argument-same-origin-destination","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}