{"record":{"id":"78eedc3f9f793b1d","repo":"jackwener/OpenCLI","slug":"label-must-be-a-positive-integer","errorCode":null,"errorMessage":"${label} must be a positive integer","messagePattern":"(.+?) must be a positive integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/1point3acres/utils.js","lineNumber":32,"sourceCode":"\n/**\n * Validate `limit` per typed-fail-fast convention (no silent clamp).\n * Throws ArgumentError on non-positive / non-integer / out-of-range input.\n */\nexport function normalizeLimit(value, defaultValue, maxValue, label = 'limit') {\n    const limit = normalizePositiveInteger(value, defaultValue, label);\n    if (limit > maxValue) {\n        throw new ArgumentError(`${label} must be <= ${maxValue}`);\n    }\n    return limit;\n}\n\n/** Validate a positive integer argument without silently flooring/clamping. */\nexport function normalizePositiveInteger(value, defaultValue, label = 'value', { min = 1 } = {}) {\n    const raw = value ?? defaultValue;\n    const limit = Number(raw);\n    if (!Number.isInteger(limit) || limit <= 0) {\n        throw new ArgumentError(`${label} must be a positive integer`);\n    }\n    if (limit < min) {\n        throw new ArgumentError(`${label} must be >= ${min}`);\n    }\n    return limit;\n}\n\nconst UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0 Safari/537.36';\n\n/** Fetch a GBK-encoded Discuz page and return decoded UTF-8 HTML. */\nexport async function fetchHtml(url, { headers = {}, cookie = '' } = {}) {\n    let res;\n    try {\n        res = await fetch(url, {\n            headers: {\n                'User-Agent': UA,\n                'Accept': 'text/html,application/xhtml+xml',\n                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/1point3acres/utils.js#L14-L50","documentation":"ArgumentError from normalizePositiveInteger, thrown when a validated option (limit, page, contentLimit, etc.) is not an integer or is <= 0. The library refuses to floor, clamp, or default invalid input silently — fail-fast validation per typed-fail-fast convention.","triggerScenarios":"Passing page: 0, limit: -1, a float like 1.5, a non-numeric string like 'ten', NaN, or null combined with no default applying (note: null falls back to defaultValue, but undefined with no default → raw undefined → Number() = NaN → throws).","commonSituations":"Off-by-one loops starting at page 0; parsing CLI flags into floats ('3.0' passes Number.isInteger after Number()? yes — but '3.5' fails); string values from query params not parsed with parseInt; arithmetic producing NaN (e.g. undefined * 2).","solutions":["Ensure the value is an integer > 0 before calling: Number.isInteger(v) && v > 0","Parse CLI/URL input with parseInt/Number and check isNaN first","Use page: 1 as the first page, not 0","Check for NaN sources: an undefined variable used in arithmetic","If a non-positive value is legitimate in your flow, guard it before calling rather than relying on the library default"],"exampleFix":"// before\nthread({ tid: '123', page: 0 })  // ArgumentError: page must be a positive integer\n// after\nthread({ tid: '123', page: 1 })","handlingStrategy":"validation","validationCode":"const toPositiveInt = (v) => {\n  const n = Number(v);\n  return Number.isInteger(n) && n > 0 ? n : null;\n};\nconst page = toPositiveInt(rawPage); if (page === null) { /* fix input */ }","typeGuard":"const isPositiveInt = (v) => typeof v === 'number' && Number.isInteger(v) && v > 0;","tryCatchPattern":"try {\n  await thread({ tid, page, limit });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be a positive integer/.test(e.message)) {\n    console.error(`${e.message} — check that ${e.message.split(' ')[0]} is an integer > 0, not a float, string, or NaN`);\n  } else throw e;\n}","preventionTips":["Pages are 1-indexed — never pass page: 0","Coerce CLI/query-string input with Number() and validate isNaN before calling","Watch for NaN from arithmetic on undefined variables","Validate all numeric options at the boundary in one place"],"tags":["argument-validation","type-error","fail-fast"],"backgroundTag":"invalid-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}