{"record":{"id":"65077822dfb5faf6","repo":"jackwener/OpenCLI","slug":"maxscrolls-must-be-a-positive-integer-got-json","errorCode":null,"errorMessage":"maxScrolls must be a positive integer, got ${JSON.stringify(maxScrolls)}","messagePattern":"maxScrolls must be a positive integer, got (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/search.js","lineNumber":544,"sourceCode":" * ~5-7 notes per scroll, so the previous `times: 2` capped extraction at\n * ~13 items regardless of `--limit` (see #1471). This helper drives scrolls\n * dynamically:\n *\n *   - count visible `section.note-item` rows (excluding related-search\n *     `.query-note-item` rows)\n *   - if count >= targetCount → break (got enough)\n *   - if two consecutive scrolls add no new rows → break (DOM plateaued,\n *     no more lazy-load available)\n *   - hard cap at `maxScrolls` iterations (default 15) to bound runtime\n *\n * Exported so the rednote adapter (same DOM shape) can reuse it.\n */\nexport function buildScrollUntilJs(targetCount, maxScrolls = 15) {\n    if (!Number.isSafeInteger(targetCount) || targetCount < 1) {\n        throw new ArgumentError(`targetCount must be a positive integer, got ${JSON.stringify(targetCount)}`);\n    }\n    if (!Number.isSafeInteger(maxScrolls) || maxScrolls < 1) {\n        throw new ArgumentError(`maxScrolls must be a positive integer, got ${JSON.stringify(maxScrolls)}`);\n    }\n    return `\n      (async () => {\n        const isVisibleNote = (el) => {\n          if (el.classList.contains('query-note-item')) return false;\n          const rect = el.getBoundingClientRect();\n          if (rect.width <= 0 || rect.height <= 0) return false;\n          const style = getComputedStyle(el);\n          return style.display !== 'none' && style.visibility !== 'hidden';\n        };\n        // Note containers: legacy \\`section.note-item\\` first, fallback to\n        // any \\`<section>\\` that wraps a search-result/explore note link\n        // (#1506 reports the class being dropped on some xhs renders).\n        const collectNoteCards = () => {\n          const classMatches = document.querySelectorAll('section.note-item');\n          if (classMatches.length > 0) return classMatches;\n          const sections = new Set();\n          for (const a of document.querySelectorAll('a[href*=\"/search_result/\"], a[href*=\"/explore/\"]')) {","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/search.js#L526-L562","documentation":"buildScrollUntilJs validates that maxScrolls is a safe integer >= 1 (default 15) and throws this ArgumentError otherwise. maxScrolls caps how many scroll iterations the generated IIFE performs to bound runtime, so a zero/negative/non-integer value is rejected before any browser JS is built.","triggerScenarios":"Calling buildScrollUntilJs(targetCount, maxScrolls) with 0, a negative value, undefined, NaN, Infinity, or a float as the second argument.","commonSituations":"Overriding the default with a config value that is unset (undefined) or a string from YAML/JSON config; passing 0 intending 'no extra scrolls'; a computation producing NaN due to earlier string math.","solutions":["Omit the second argument to use the safe default of 15.","Pass a positive safe integer, e.g. buildScrollUntilJs(50, 30) for more scrolls.","Validate/parse config values before passing: Number.isSafeInteger(cfg.maxScrolls).","Clamp with Math.max(1, Math.trunc(n)) when deriving the cap dynamically."],"exampleFix":"// before\nbuildScrollUntilJs(limit, config.scrollMax);\n// after\nconst maxScrolls = Number.isSafeInteger(config.scrollMax) && config.scrollMax >= 1\n  ? config.scrollMax : 15;\nbuildScrollUntilJs(limit, maxScrolls);","handlingStrategy":"validation","validationCode":"function assertMaxScrolls(v) {\n  if (!Number.isSafeInteger(v) || v < 1) {\n    throw new TypeError(`maxScrolls must be a positive safe integer, got ${JSON.stringify(v)}`);\n  }\n}\nassertMaxScrolls(maxScrolls ?? 15);","typeGuard":"const isValidMaxScrolls = (v) => Number.isSafeInteger(v) && v >= 1;","tryCatchPattern":null,"preventionTips":["Rely on the default (15) unless you have a measured reason to change it.","Validate YAML/JSON config numbers — they often arrive as strings or null.","Never pass 0 expecting 'unlimited'; maxScrolls must be >= 1.","Clamp derived values with Math.max(1, Math.trunc(v)).","Type-check callers (JSDoc/TS) so non-number args fail at review time."],"tags":["argument-validation","typeerror","api-misuse"],"backgroundTag":"invalid-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}