{"record":{"id":"539d35adbbd3889f","repo":"jackwener/OpenCLI","slug":"return-ret-must-be-on-or-after-depart","errorCode":null,"errorMessage":"--return (${ret}) must be on or after --depart (${depart})","messagePattern":"--return \\((.+?)\\) must be on or after --depart \\((.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/ctrip/flight-round.js","lineNumber":74,"sourceCode":"    ],\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 depart = parseIsoDate('depart', kwargs.depart);\n        const ret = parseIsoDate('return', kwargs.return);\n        if (ret < depart) {\n            throw new ArgumentError(`--return (${ret}) must be on or after --depart (${depart})`);\n        }\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl =\n            `https://flights.ctrip.com/online/list/round-${fromCode.toLowerCase()}-${toCode.toLowerCase()}` +\n            `?depdate=${depart}_${ret}&cabin=Y_S_C_F&adult=1&child=0&infant=0`;\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_FLIGHTS_ROUND_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('flights.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 round-trip flight page did not render flight cards (state=${String(waitResult)})`);\n        }\n        const renderedCardCount = await page.evaluate(buildScrollUntilJs(ROUND_CARD_SELECTOR, limit));\n        const raw = await page.evaluate(buildFlightExtractJs(ROUND_CARD_SELECTOR, false));\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Ctrip round-trip flight DOM extraction returned malformed rows');","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/ctrip/flight-round.js#L56-L92","documentation":"An ArgumentError raised when the parsed --return date is earlier than the parsed --depart date. Both dates go through parseIsoDate first, so this compares normalized ISO strings (YYYY-MM-DD lexicographic comparison is chronologically correct). A round-trip search cannot return before it departs.","triggerScenarios":"Calling flight-round with a return date strictly before the depart date, e.g. `--depart 2026-03-10 --return 2026-03-05`.","commonSituations":"Mixing up argument order in scripts or positional args; computing the return date from the wrong base variable; timezone bugs that shift a computed return date a day backward; user input where the user thinks of depart/return reversed.","solutions":["Swap or correct the dates so --return is on or after --depart.","Validate the pair in your calling code before invoking the CLI.","If the return date is computed, check the timezone/arithmetic that produced it.","Note the error message includes both dates — verify which one is wrong against the logged values."],"exampleFix":"// before\ncli(['flight-round', '--depart', ret, '--return', dep]);\n// after\ncli(['flight-round', '--depart', dep, '--return', ret]); // ret >= dep validated before call","handlingStrategy":"validation","validationCode":"function validateRoundTrip(depart, ret) {\n  const iso = /^\\d{4}-\\d{2}-\\d{2}$/;\n  if (!iso.test(depart) || !iso.test(ret)) throw new Error('dates must be YYYY-MM-DD');\n  if (ret < depart) throw new Error(`--return (${ret}) must be on or after --depart (${depart})`);\n}","typeGuard":"function isOnOrAfter(ret, depart) { return /^\\d{4}-\\d{2}-\\d{2}$/.test(ret) && /^\\d{4}-\\d{2}-\\d{2}$/.test(depart) && ret >= depart; }","tryCatchPattern":"try { await cli(['flight-round','--depart',dep,'--return',ret]); } catch (e) { if (e instanceof ArgumentError && e.message.includes('--return')) { logDateValidationError(dep, ret); } else throw e; }","preventionTips":["Validate depart <= return in the caller before invoking the CLI.","Be careful with timezones when computing dates; format to YYYY-MM-DD explicitly.","Check argument order when building CLI invocations programmatically.","Reuse one date-normalization helper for both flags."],"tags":["argument-validation","date-validation","ctrip","cli"],"backgroundTag":"invalid-date-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}