{"record":{"id":"d8b9364689b9e8af","repo":"jackwener/OpenCLI","slug":"juejin-cursor-must-be-a-non-negative-decimal-integ","errorCode":null,"errorMessage":"juejin cursor must be a non-negative decimal integer","messagePattern":"juejin cursor must be a non-negative decimal integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/juejin/utils.js","lineNumber":59,"sourceCode":"        n = Number(raw);\n    }\n    else {\n        throw new ArgumentError(`juejin ${label} must be a positive decimal integer`);\n    }\n    if (!Number.isSafeInteger(n) || n <= 0) {\n        throw new ArgumentError(`juejin ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`juejin ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport function requireCursor(value) {\n    const raw = value ?? '0';\n    if (typeof raw === 'number') {\n        if (Number.isSafeInteger(raw) && raw >= 0) return String(raw);\n        throw new ArgumentError('juejin cursor must be a non-negative decimal integer');\n    }\n    if (typeof raw === 'string' && /^(0|[1-9]\\d*)$/.test(raw)) {\n        return raw;\n    }\n    throw new ArgumentError('juejin cursor must be a non-negative decimal integer');\n}\n\n/** Resolve a `--category` arg to the underlying numeric category id. */\nexport function resolveCategory(value) {\n    if (value == null) return '';\n    const raw = String(value).trim();\n    if (!raw) return '';\n    if (JUEJIN_ID.test(raw)) return raw;\n    const slug = raw.toLowerCase();\n    if (CATEGORY_ALIASES[slug]) return CATEGORY_ALIASES[slug];\n    throw new ArgumentError(\n        `juejin category \"${value}\" is not recognised`,\n        `Use a category id (e.g. \"${CATEGORY_ALIASES.backend}\") or one of: ${Object.keys(CATEGORY_ALIASES).join(', ')}.`,","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/juejin/utils.js#L41-L77","documentation":"requireCursor normalizes pagination cursors and throws an ArgumentError when a numeric cursor is not a non-negative safe integer. Negative numbers, floats, Infinity/NaN, and unsafe integers are all rejected, since Juejin article cursors are offsets/ids expressed as non-negative decimal integers.","triggerScenarios":"Passing --cursor -1, --cursor 1.5, --cursor 1e21, or a float to a command option that routes through requireCursor; programmatically passing a computed number that is negative or non-integer.","commonSituations":"Arithmetic on cursors producing negatives (cursor - pageSize when at the first page); JSON numbers parsed as floats; JavaScript precision loss converting a big cursor id to a float that is not a safe integer.","solutions":["Pass a valid non-negative integer cursor (e.g. --cursor 0 to start over).","Clamp/round before calling: n = Math.max(0, Math.floor(n)).","Use the next_cursor value from the previous response verbatim instead of doing arithmetic on cursors.","For very large ids, pass them as strings matching /^(0|[1-9]\\d*)$/ to avoid float precision loss."],"exampleFix":"// before\nconst next = Number(cursor) - pageSize; // can go negative\n// after\nconst next = Math.max(0, Number(cursor) - pageSize);\ncli({ cursor: String(next) });","handlingStrategy":"type-guard","validationCode":"function validNumberCursor(n){ return typeof n === 'number' && Number.isSafeInteger(n) && n >= 0; }\n// if (!validNumberCursor(cursor)) cursor = 0;","typeGuard":"function isNonNegativeSafeInt(v){ return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0; }","tryCatchPattern":"try {\n  cli({ cursor });\n} catch (e) {\n  if (/cursor must be a non-negative decimal integer/.test(e.message)) {\n    cli({ cursor: '0' }); // restart pagination\n  } else throw e;\n}","preventionTips":["Never do arithmetic on cursors; always pass through the server-provided next_cursor.","Clamp with Math.max(0, Math.floor(n)) when cursors are derived numerically.","Keep large ids/cursors as strings to dodge float precision loss."],"tags":["argument-validation","pagination","cursor","integer"],"backgroundTag":"invalid-numeric-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}