{"record":{"id":"8f61e2ce0b87c33f","repo":"jackwener/OpenCLI","slug":"from-and-to-must-differ-got-fromcode-8f61e2","errorCode":null,"errorMessage":"--from and --to must differ (got ${fromCode})","messagePattern":"--from and --to must differ \\(got (.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"warning","filePath":"clis/ctrip/flight.js","lineNumber":172,"sourceCode":"    args: [\n        { name: 'from', required: true, positional: true, help: 'Departure IATA code (e.g. BJS / PEK)' },\n        { name: 'to', required: true, positional: true, help: 'Arrival IATA code (e.g. SHA / PVG)' },\n        { name: 'date', required: true, help: 'Departure date (YYYY-MM-DD)' },\n        { name: 'limit', default: DEFAULT_LIMIT, help: `Number of flights (${MIN_LIMIT}-${MAX_LIMIT})` },\n    ],\n    columns: [\n        'rank',\n        'airline', 'flightNo', 'aircraft',\n        'departureTime', 'departureAirport',\n        'arrivalTime', 'arrivalAirport', 'terminal',\n        'price', 'currency', 'cabin',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const fromCode = parseIataCode('from', kwargs.from);\n        const toCode = parseIataCode('to', kwargs.to);\n        if (fromCode === toCode) {\n            throw new ArgumentError(`--from and --to must differ (got ${fromCode})`);\n        }\n        const date = parseIsoDate('date', kwargs.date);\n        const limit = parseFlightLimit(kwargs.limit);\n\n        const searchUrl =\n            `https://flights.ctrip.com/online/list/oneway-${fromCode.toLowerCase()}-${toCode.toLowerCase()}` +\n            `?depdate=${date}&cabin=Y_S_C_F&adult=1&child=0&infant=0`;\n        if (typeof page?.startNetworkCapture !== 'function' ||\n            typeof page?.readNetworkCapture !== 'function' ||\n            !await page.startNetworkCapture(CAPTURE_PATTERN)) {\n            throw new CommandExecutionError('Ctrip flight requires browser response interception');\n        }\n        await page.readNetworkCapture();\n        await page.goto(searchUrl);\n        // The initial document can finish before the large batchSearch body.\n        // The first rendered card is only a readiness signal; row data still\n        // comes exclusively from the structured response below.\n        const readiness = await page.evaluate(WAIT_FOR_BATCH_CAPTURE_JS);","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight.js#L154-L190","documentation":"A pure client-side ArgumentError raised before any network activity: the `ctrip flight` command requires --from and --to to be different IATA codes, because the oneway results URL (`oneway-<from>-<to>`) is meaningless when both endpoints are the same and Ctrip would not serve a useful list. The message echoes the offending code so you can see which value was reused.","triggerScenarios":"Calling `ctrip flight BJS BJS --date ...` (or any identical from/to IATA codes, e.g. `PEK PEK`). The check compares parseIataCode('from') === parseIataCode('to') in the command's func at clis/ctrip/flight.js:171.","commonSituations":"Scripting a loop over routes where a placeholder variable was never replaced; typo where both positional args got the same city; generating routes programmatically and including self-routes by accident; testing the CLI with the same value twice to 'just see it run'.","solutions":["Pass two distinct IATA codes, e.g. `ctrip flight BJS SHA --date 2026-09-01`.","If building routes in a script, filter out pairs where from === to before invoking the command.","If you actually need flights departing from one airport and returning, use the sibling `flight-round` (round-trip) command instead of oneway."],"exampleFix":"// before\nawait cliRun(['flight', 'PEK', 'PEK', '--date', '2026-09-01']);\n\n// after: guard before calling\nif (from === to) throw new Error('from and to must differ');\nawait cliRun(['flight', from, to, '--date', '2026-09-01']);","handlingStrategy":"validation","validationCode":"function validateRoute(from, to) {\n  if (String(from).toUpperCase() === String(to).toUpperCase()) {\n    throw new Error(`route endpoints must differ (got ${from})`);\n  }\n}\nvalidateRoute(from, to);","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await ctripFlight({ from, to, date });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('must differ')) {\n    console.error(`Bad route: ${e.message}`); process.exitCode = 2; return [];\n  }\n  throw e;\n}","preventionTips":["Validate from !== to at the top of any script that builds route lists.","Filter self-routes (from === to) out of programmatically generated pairs.","Use the round-trip command for A→B→A needs instead of forcing oneway.","Echo the full command line in logs so identical positional args are obvious."],"tags":["argument-validation","cli-input","user-input"],"backgroundTag":"invalid-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}