{"record":{"id":"86866c861f4ff4a4","repo":"jackwener/OpenCLI","slug":"endoflife-label-must-be-a-positive-integer","errorCode":null,"errorMessage":"`endoflife ${label} must be a positive integer`","messagePattern":"`endoflife (.+?) must be a positive integer`","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/endoflife/utils.js","lineNumber":34,"sourceCode":"        throw new ArgumentError(\n            'endoflife product is required (e.g. \"nodejs\", \"python\", \"ubuntu\")',\n            'Use the slug visible at https://endoflife.date/<product>.',\n        );\n    }\n    if (!PRODUCT.test(s)) {\n        throw new ArgumentError(\n            `endoflife product \"${value}\" is not a valid endoflife.date slug`,\n            'Slugs are lowercase ASCII letters/digits/\"._-\", e.g. \"nodejs\", \"python\", \"ubuntu\".',\n        );\n    }\n    return s;\n}\n\nexport function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {\n    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`endoflife ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`endoflife ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport async function eolFetch(url, label) {\n    let resp;\n    try {\n        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });\n    }\n    catch (err) {\n        throw new CommandExecutionError(\n            `${label} request failed: ${err?.message ?? err}`,\n            'Check that endoflife.date is reachable from this network.',\n        );\n    }","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/endoflife/utils.js#L16-L52","documentation":"requireBoundedInt coerces its input to a number and enforces it is an integer within (0, maxValue]. Non-integers, zero, negatives, NaN (e.g. non-numeric strings) throw an ArgumentError '<label> must be a positive integer'; values above the cap throw a separate 'must be <= max' error. Used for options like limit.","triggerScenarios":"Passing limit=0, limit=-5, limit='abc', limit=2.5, or an empty string that coerces to NaN when calling an endoflife CLI command that uses requireBoundedInt.","commonSituations":"Users typing '--limit 0' expecting unlimited, scripts computing a limit that becomes NaN from missing data, locale-formatted numbers ('1,000') that fail Number(), float results from division feeding the option.","solutions":["Pass a positive whole number within the option's documented maximum","Clamp/round the value in your script: Math.max(1, Math.min(Math.floor(x), max))","Check the source value isn't an empty or non-numeric string","Omit the option to use the default value"],"exampleFix":"// before\nproduct({ limit: 'all' }); // NaN -> error\n// after\nproduct({ limit: 20 });","handlingStrategy":"validation","validationCode":"function toBoundedInt(v, def, max) {\n  const n = Number(v ?? def);\n  if (!Number.isInteger(n) || n <= 0 || n > max) throw new Error(`value must be an integer in 1..${max}`);\n  return n;\n}","typeGuard":"function isPositiveInt(v) { return typeof v === 'number' && Number.isInteger(v) && v > 0; }","tryCatchPattern":"try {\n  await eolProduct('nodejs', { limit: opts.limit });\n} catch (e) {\n  if (e instanceof ArgumentError && /positive integer|must be <=/.test(e.message)) { console.error('limit must be 1..max'); process.exitCode = 2; }\n  else throw e;\n}","preventionTips":["Sanitize numeric options: Math.max(1, Math.min(Math.floor(Number(x) || def), max))","Avoid locale-formatted number strings ('1,000') in arguments","Never pass 0 or negative values expecting 'unlimited' — omit the option for defaults","Document valid ranges in wrapper scripts and validate before invoking"],"tags":["validation","argument","integer","endoflife"],"backgroundTag":"invalid-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}