{"record":{"id":"343ed3fdbbffc133","repo":"jackwener/OpenCLI","slug":"page-must-be-a-positive-decimal-integer-got","errorCode":null,"errorMessage":"--page must be a positive decimal integer, got: ${value}","messagePattern":"--page must be a positive decimal integer, got: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/utils.js","lineNumber":88,"sourceCode":"                }\n            }\n            res.resume();\n            reject(new Error(`Cannot resolve BV ID from short URL: ${trimmed}`));\n        });\n        req.on('error', reject);\n        req.setTimeout(4000, () => { req.destroy(); reject(new Error(`Timeout resolving short URL: ${trimmed}`)); });\n    });\n}\n/**\n * 解析 --page 选集序号（分P / 视频选集）。\n * 缺省/空串 → null（不下钻，保持整集默认 P1 旧行为）。\n * 非正十进制整数 → 抛 ArgumentError（参数错误，不静默吞）。\n */\nexport function parsePageArg(value) {\n    if (value == null || value === '') return null;\n    if (typeof value === 'number') {\n        if (Number.isSafeInteger(value) && value >= 1) return value;\n        throw new ArgumentError(`--page must be a positive decimal integer, got: ${value}`);\n    }\n    if (typeof value !== 'string' || !/^[1-9]\\d*$/.test(value)) {\n        throw new ArgumentError(`--page must be a positive decimal integer, got: ${String(value)}`);\n    }\n    const n = Number(value);\n    if (!Number.isSafeInteger(n)) {\n        throw new ArgumentError(`--page is too large: ${value}`);\n    }\n    return n;\n}\n\nfunction readApiPositiveInteger(value, label) {\n    if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 1) {\n        return value;\n    }\n    if (typeof value === 'string' && /^[1-9]\\d*$/.test(value)) {\n        const n = Number(value);\n        if (Number.isSafeInteger(n)) return n;","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/utils.js#L70-L106","documentation":"parsePageArg validates the --page argument as a positive safe integer. When a number is passed that is not a safe integer >= 1 (0, negative, fractional, NaN, Infinity, or beyond Number.MAX_SAFE_INTEGER), it throws this ArgumentError. null/empty returns null (meaning default P1 behavior).","triggerScenarios":"Calling parsePageArg(0), parsePageArg(-1), parsePageArg(1.5), parsePageArg(NaN/Infinity), or a number larger than Number.MAX_SAFE_INTEGER.","commonSituations":"Computing the page number with a formula that yields 0 or a float (e.g. an off-by-one loop, division); a config value defaulting to 0; parsing user input with parseFloat instead of integer parsing upstream.","solutions":["Pass an integer >= 1 (page numbering is 1-based)","Round/validate upstream: Number.isSafeInteger(n) && n >= 1","Pass null/undefined or empty string if you want the default (P1) behavior instead of 0","Fix off-by-one arithmetic in the calling script"],"exampleFix":"// before\nconst page = Math.floor(totalParts / 2); // could be 0\nawait parsePageArg(page);\n// after\nconst page = Math.max(1, Math.floor(totalParts / 2));\nawait parsePageArg(page);","handlingStrategy":"validation","validationCode":"function safePage(n) {\n  return (Number.isSafeInteger(n) && n >= 1) ? n : null; // null = use default\n}\nconst page = safePage(rawPage);","typeGuard":"function isPositiveSafeInteger(v) {\n  return typeof v === 'number' && Number.isSafeInteger(v) && v >= 1;\n}","tryCatchPattern":"try {\n  const page = parsePageArg(value);\n} catch (err) {\n  if (err instanceof ArgumentError && /--page/.test(err.message)) {\n    console.error(`Invalid --page '${value}'; defaulting to P1`);\n    const page = null;\n  } else throw err;\n}","preventionTips":["Page numbers are 1-based; guard loops with Math.max(1, ...)","Use Number.isSafeInteger checks before passing computed page numbers","Pass null/undefined (or omit) rather than 0 for the default part","Parse CLI input with parseInt(value, 10), never parseFloat"],"tags":["argument-validation","cli-input","bilibili"],"backgroundTag":"invalid-page-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}