{"record":{"id":"03e5ffadd5dd4a17","repo":"jackwener/OpenCLI","slug":"checkout-checkout-must-be-after-checkin-ch","errorCode":null,"errorMessage":"checkout (${checkout}) must be after checkin (${checkin})","messagePattern":"checkout \\((.+?)\\) must be after checkin \\((.+?)\\)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/booking/search.js","lineNumber":245,"sourceCode":"    'name',\n    'country',\n    'slug',\n    'star_rating',\n    'review_score',\n    'review_count',\n    'price_amount',\n    'price_currency',\n    'distance',\n    'recommended_room',\n    'url',\n  ],\n  func: async (page, kwargs) => {\n    const destination = String(kwargs.destination || '').trim();\n    if (!destination) throw new ArgumentError('destination is required');\n    const checkin = normalizeDate(kwargs.checkin, 'checkin');\n    const checkout = normalizeDate(kwargs.checkout, 'checkout');\n    if (checkin >= checkout) {\n      throw new ArgumentError(`checkout (${checkout}) must be after checkin (${checkin})`);\n    }\n    const adults = normalizePositiveInt(kwargs.adults, 2, 'adults', 30);\n    const rooms = normalizePositiveInt(kwargs.rooms, 1, 'rooms', 30);\n    const children = normalizeNonNegativeInt(kwargs.children, 0, 'children', 10);\n    const currency = normalizeCurrency(kwargs.currency);\n    const lang = normalizeLang(kwargs.lang);\n    const limit = normalizePositiveInt(kwargs.limit, 25, 'limit', 100);\n    const offset = normalizeNonNegativeInt(kwargs.offset, 0, 'offset', 1000);\n\n    const url = buildSearchUrl({ destination, checkin, checkout, adults, rooms, children, offset, currency, lang });\n\n    try {\n      await page.goto(url);\n    } catch (err) {\n      throw new CommandExecutionError(`Failed to load Booking.com search page: ${err?.message || err}`);\n    }\n\n    // Booking lazy-loads price cells; wait for at least the first card price to settle.","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/booking/search.js#L227-L263","documentation":"This ArgumentError is thrown by the booking search command when the checkout date is not strictly after the checkin date. The library normalizes both dates to a comparable form and performs a simple string/lexicographic comparison (checkin >= checkout), so equal or inverted ranges are rejected before any browser automation starts. It exists to prevent building a Booking.com URL with an invalid date range that would yield no results or an error page.","triggerScenarios":"Calling the search command with checkin equal to checkout (e.g. checkin=2026-01-10, checkout=2026-01-10), or with checkout earlier than checkin (e.g. dates swapped or checkout omitted and defaulted to a date before checkin).","commonSituations":"Users passing a single-night stay as the same date for both fields; computing dates with an off-by-one or swapped variable; passing MM/DD vs DD/MM formats so normalizeDate parses them differently than expected; passing checkout as a relative date like 'tomorrow' when checkin was 'today+2'.","solutions":["Pass a checkout date strictly after checkin (at least one night apart).","Swap the values if they were accidentally reversed.","Verify date format expected by normalizeDate (ISO yyyy-mm-dd) and convert before calling.","If a same-day booking is intended, this API does not support it; check for a dedicated option or use a different tool."],"exampleFix":"// before\nawait run('booking.search', { destination: 'Paris', checkin: '2026-05-10', checkout: '2026-05-10' });\n// after\nawait run('booking.search', { destination: 'Paris', checkin: '2026-05-10', checkout: '2026-05-11' });","handlingStrategy":"validation","validationCode":"function assertValidStay(checkin, checkout) {\n  const ckin = new Date(checkin), ckout = new Date(checkout);\n  if (isNaN(ckin) || isNaN(ckout)) throw new Error('dates must be valid');\n  if (ckout <= ckin) throw new Error(`checkout (${checkout}) must be after checkin (${checkin})`);\n}\n// call before the API:\nassertValidStay('2026-05-10', '2026-05-11');","typeGuard":"const isAfter = (checkin, checkout) => String(checkout) > String(checkin); // ISO dates compare lexicographically","tryCatchPattern":"try {\n  await booking.search({ destination, checkin, checkout });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be after checkin/.test(e.message)) {\n    [checkin, checkout] = [checkin, addDays(checkin, 1)]; // or surface to user\n  } else throw e;\n}","preventionTips":["Always compute checkout as checkin + N nights in one place in your code.","Use ISO yyyy-mm-dd strings for both dates to avoid format ambiguity.","Add a unit test asserting checkout > checkin before invoking the CLI.","Never allow the same value for checkin and checkout in your UI/CLI wrapper."],"tags":["validation","argument-error","dates"],"backgroundTag":"invalid-date-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}