{"record":{"id":"004cf95c4ee5b072","repo":"jackwener/OpenCLI","slug":"label-must-be-a-non-negative-integer","errorCode":null,"errorMessage":"${label} must be a non-negative integer","messagePattern":"(.+?) must be a non-negative integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/booking/search.js","lineNumber":26,"sourceCode":"const DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nfunction normalizePositiveInt(value, defaultValue, label, max) {\n  const raw = value ?? defaultValue;\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n <= 0) {\n    throw new ArgumentError(`${label} must be a positive integer`);\n  }\n  if (typeof max === 'number' && n > max) {\n    throw new ArgumentError(`${label} must be <= ${max}`);\n  }\n  return n;\n}\n\nfunction normalizeNonNegativeInt(value, defaultValue, label, max) {\n  const raw = value ?? defaultValue;\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n < 0) {\n    throw new ArgumentError(`${label} must be a non-negative integer`);\n  }\n  if (typeof max === 'number' && n > max) {\n    throw new ArgumentError(`${label} must be <= ${max}`);\n  }\n  return n;\n}\n\nfunction normalizeDate(value, label) {\n  const v = String(value || '').trim();\n  if (!v) {\n    throw new ArgumentError(`${label} is required (YYYY-MM-DD)`);\n  }\n  if (!DATE_RE.test(v)) {\n    throw new ArgumentError(`${label} must be YYYY-MM-DD, got ${JSON.stringify(value)}`);\n  }\n  const [year, month, day] = v.split('-').map(Number);\n  const d = new Date(Date.UTC(year, month - 1, day));\n  if (","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/booking/search.js#L8-L44","documentation":"normalizeNonNegativeInt requires its input to coerce (via Number()) to an integer that is 0 or greater. This ArgumentError is thrown for options like children or offset when the value is non-numeric, fractional, or negative. Zero is explicitly allowed here, unlike the positive-int variant.","triggerScenarios":"Passing children: -1, offset: -10, children: 'none', offset: 1.5, or an empty string to search. Also passing undefined when no defaultValue exists for that option.","commonSituations":"Computing offset as currentPage*size with currentPage initialized to -1; form inputs left as '-' or blank strings submitted programmatically; typos like '--children -0.5'; passing NaN from a failed parseInt without a radix/fallback.","solutions":["Pass a whole number >= 0 for the flagged option (e.g. --children 0, --offset 0).","Validate input before the call: Number.isInteger(n) && n >= 0.","Omit the option so the built-in defaultValue applies instead of passing a bad sentinel like -1.","Fix the upstream calculation producing the negative value (e.g. clamp page index to 0)."],"exampleFix":"// before\nconst offset = (page - 1) * size; // page=0 -> offset=-20\nawait bookingSearch({ offset }); // throws: offset must be a non-negative integer\n// after\nconst offset = Math.max(0, (page - 1) * size);\nawait bookingSearch({ offset });","handlingStrategy":"validation","validationCode":"function assertNonNegativeInt(value, label, max) {\n  const n = Number(value);\n  if (!Number.isInteger(n) || n < 0) throw new Error(`${label} must be a non-negative integer`);\n  if (typeof max === 'number' && n > max) throw new Error(`${label} must be <= ${max}`);\n  return n;\n}\nconst offset = assertNonNegativeInt(opts.offset ?? 0, 'offset');","typeGuard":"function isNonNegativeInt(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 0;\n}","tryCatchPattern":"try {\n  await bookingSearch({ children, offset });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('must be a non-negative integer')) {\n    console.error(`Bad numeric option: ${e.message}`); process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Clamp computed pagination values: Math.max(0, offset).","Use 0, not -1, as the 'unset' sentinel for counts.","parseInt with radix 10 and verify Number.isInteger on form/env input.","Distinguish which options allow zero (children, offset) vs require >= 1 (adults)."],"tags":["argument-validation","cli","input-validation","non-negative-integer"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}