{"record":{"id":"13d211349b995d4c","repo":"jackwener/OpenCLI","slug":"lang-must-be-one-of-allowed-langs-join","errorCode":null,"errorMessage":"lang must be one of: ${[...ALLOWED_LANGS].join(', ')}","messagePattern":"lang must be one of: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/booking/search.js","lineNumber":73,"sourceCode":"function normalizeCurrency(value) {\n  if (value == null || value === '') return '';\n  const v = String(value).trim().toUpperCase();\n  if (!/^[A-Z]{3}$/.test(v)) {\n    throw new ArgumentError(`currency must be a 3-letter ISO code (e.g. USD, JPY, CNY), got ${JSON.stringify(value)}`);\n  }\n  return v;\n}\n\nconst ALLOWED_LANGS = new Set([\n  'en-us', 'en-gb', 'zh-cn', 'zh-tw', 'ja', 'ko', 'de', 'fr', 'es', 'it',\n  'pt-br', 'pt-pt', 'ru', 'th', 'vi', 'tr', 'pl', 'nl', 'ar',\n]);\n\nfunction normalizeLang(value) {\n  if (value == null || value === '') return '';\n  const v = String(value).trim().toLowerCase();\n  if (!ALLOWED_LANGS.has(v)) {\n    throw new ArgumentError(`lang must be one of: ${[...ALLOWED_LANGS].join(', ')}`);\n  }\n  return v;\n}\n\nfunction hasPositiveResultCount(text) {\n  const value = String(text || '').replace(/\\u00a0/g, ' ');\n  const resultCount = value.match(/\\b([1-9][0-9,.\\s]*)\\s+(?:properties|property|stays|stay|hotels|hotel)\\b/i);\n  if (!resultCount) return false;\n  const digits = resultCount[1].replace(/\\D/g, '');\n  return Boolean(digits) && Number(digits) > 0;\n}\n\nfunction buildSearchUrl({\n  destination,\n  checkin,\n  checkout,\n  adults,\n  rooms,","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/booking/search.js#L55-L91","documentation":"normalizeLang validates the optional lang parameter against a fixed ALLOWED_LANGS set of BCP-47-style tags (en-us, zh-cn, ja, de, ...). The value is trimmed and lowercased first, then membership is checked; anything outside the set throws ArgumentError. Unknown or unsupported locale codes are rejected to keep the underlying site's language parameter valid.","triggerScenarios":"Passing lang values like 'en', 'en-US-x-variant', 'zh', 'zh-Hans', 'pt', 'fr-CA', or a wrong-case-with-typo value not in the set ('enn-us'). Note 'en' and 'zh' alone are NOT accepted — only the listed tags.","commonSituations":"Using a bare language code ('en') instead of the required regional form ('en-us'); users in regions with variants not in the allowlist (fr-CA, es-MX); default browser locale 'en' passed straight through; newer locale tags unsupported by this allowlist.","solutions":["Change the value to one of the allowed tags, e.g. 'en-us' instead of 'en', 'zh-cn' instead of 'zh'","Map your app's locale to the nearest supported tag before calling (fr-CA -> fr, es-MX -> es)","Trim and lowercase the value to rule out accidental case/whitespace mismatches","If a needed locale is missing, extend ALLOWED_LANGS in clis/booking/search.js"],"exampleFix":"// before\nconst lang = 'en';\nawait search(page, { destination: 'Tokyo', lang });\n// after\nconst lang = 'en-us';\nawait search(page, { destination: 'Tokyo', lang });","handlingStrategy":"type-guard","validationCode":"const ALLOWED = new Set(['en-us','en-gb','zh-cn','zh-tw','ja','ko','de','fr','es','it','pt-br','pt-pt','ru','th','vi','tr','pl','nl','ar']);\nfunction normalizeLangSafe(v) {\n  if (v == null || v === '') return '';\n  const s = String(v).trim().toLowerCase();\n  if (!ALLOWED.has(s)) throw new Error(`unsupported lang: ${v}`);\n  return s;\n}","typeGuard":"function isSupportedLang(v) {\n  const s = String(v ?? '').trim().toLowerCase();\n  return s === '' || ['en-us','en-gb','zh-cn','zh-tw','ja','ko','de','fr','es','it','pt-br','pt-pt','ru','th','vi','tr','pl','nl','ar'].includes(s);\n}","tryCatchPattern":"try {\n  await search(page, { destination, lang });\n} catch (e) {\n  if (/lang must be one of/.test(e.message)) {\n    lang = mapToSupportedLang(lang); // e.g. 'en' -> 'en-us'\n  } else throw e;\n}","preventionTips":["Use the exact regional tags from ALLOWED_LANGS ('en-us', not 'en')","Lowercase and trim locale values before passing them","Maintain a locale->supported-tag mapping for regional variants like fr-CA or es-MX","Keep this set in sync if the library updates its allowlist"],"tags":["validation","argument-error","locale","allowlist"],"backgroundTag":"unsupported-locale","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}