{"record":{"id":"2518d30b8cdf9ccd","repo":"jackwener/OpenCLI","slug":"checkin-must-be-before-checkout-got-checkin","errorCode":null,"errorMessage":"--checkin must be before --checkout (got ${checkin} .. ${checkout})","messagePattern":"--checkin must be before --checkout \\(got (.+?) \\.\\. (.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/trip/hotel-search.js","lineNumber":47,"sourceCode":"    args: [\n        { name: 'city', required: true, positional: true, help: 'Numeric Trip.com city id (discover via the hotels search box; e.g. 338 for London)' },\n        { name: 'checkin', required: true, help: 'Check-in date (YYYY-MM-DD)' },\n        { name: 'checkout', required: true, help: 'Check-out date (YYYY-MM-DD)' },\n        { name: 'limit', type: 'int', default: 20, help: 'Number of hotels (1-50)' },\n    ],\n    columns: [\n        'rank',\n        'name', 'score', 'reviewLabel', 'reviews',\n        'location', 'room',\n        'price', 'currency',\n        'url',\n    ],\n    func: async (page, kwargs) => {\n        const cityId = parseCityId('city', kwargs.city);\n        const checkin = parseIsoDate('checkin', kwargs.checkin);\n        const checkout = parseIsoDate('checkout', kwargs.checkout);\n        if (checkin >= checkout) {\n            throw new ArgumentError(`--checkin must be before --checkout (got ${checkin} .. ${checkout})`);\n        }\n        const limit = parseListLimit(kwargs.limit);\n\n        const searchUrl = buildHotelSearchUrl(cityId, checkin, checkout);\n        await page.goto(searchUrl);\n        const waitResult = await page.evaluate(WAIT_FOR_HOTELS_JS);\n        if (waitResult === 'captcha') {\n            throw new AuthRequiredError('trip.com', 'Trip.com is asking for a verification; complete it in your browser session and retry');\n        }\n        if (waitResult !== 'content') {\n            throw new CommandExecutionError(`Trip.com hotel page did not render hotel cards (state=${String(waitResult)})`);\n        }\n        const raw = await page.evaluate(buildHotelExtractJs());\n        if (!Array.isArray(raw)) {\n            throw new CommandExecutionError('Trip.com hotel DOM extraction returned malformed rows');\n        }\n        if (raw.length === 0) {\n            throw new EmptyResultError('trip hotel-search', `No hotels for city ${cityId} on ${checkin} .. ${checkout}`);","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/trip/hotel-search.js#L29-L65","documentation":"This ArgumentError from clis/trip/hotel-search.js is thrown during argument validation when the parsed --checkin date is not strictly before --checkout (parseIsoDate output comparison checkin >= checkout). Trip.com hotel searches require a positive stay length, so the command fails before making any network request. Both dates appear in the message for easy diagnosis.","triggerScenarios":"Invoking hotel-search with --checkin and --checkout that are equal or inverted, e.g. --checkin 2026-09-10 --checkout 2026-09-10 or --checkin 2026-09-15 --checkout 2026-09-10.","commonSituations":"Same-day 'day use' bookings mistakenly given identical dates; swapped arguments in a script or shell alias; timezone/time parsing making two intended-different dates normalize to the same ISO date; copy-paste errors leaving checkout as an older date.","solutions":["Correct the arguments so checkout is at least one day after checkin.","Add a pre-flight check in calling scripts: if (!(checkin < checkout)) fail early with a clear message.","If same-day stays are intended, use a hotel day-use product or add one night, since this command does not support zero-night stays.","Ensure date inputs are unambiguous ISO strings to avoid normalization collisions."],"exampleFix":"// before\nawait runTripHotelSearch({ city: 'shanghai', checkin: '2026-09-10', checkout: '2026-09-10' });\n// after\nconst checkin = '2026-09-10', checkout = '2026-09-11';\nif (!(new Date(checkin) < new Date(checkout))) throw new Error('checkin must precede checkout');\nawait runTripHotelSearch({ city: 'shanghai', checkin, checkout });","handlingStrategy":"validation","validationCode":"const ci = new Date(checkin), co = new Date(checkout);\nif (isNaN(ci) || isNaN(co)) throw new Error('checkin/checkout must be ISO dates');\nif (ci >= co) throw new Error(`--checkin must be before --checkout (got ${checkin} .. ${checkout})`);","typeGuard":"null","tryCatchPattern":"try {\n  await runTripHotelSearch(args);\n} catch (e) {\n  if (e instanceof ArgumentError && /checkin/.test(e.message)) {\n    console.error(`Fix date range: ${e.message}`);\n    process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Always pass explicit ISO date strings, never locale-dependent formats.","Validate ci < co in wrapper scripts before invoking.","Watch for swapped argument order when building commands programmatically.","Remember same-day stays need checkout = checkin + 1 day minimum."],"tags":["argument-validation","dates","input-error","trip-com"],"backgroundTag":"invalid-date-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}