jackwener/OpenCLI · error · ArgumentError

maxScrolls must be a positive integer, got ${JSON.stringify(

Error message

maxScrolls must be a positive integer, got ${JSON.stringify(maxScrolls)}

What it means

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.

Source

Thrown at clis/xiaohongshu/search.js:544

 * ~5-7 notes per scroll, so the previous `times: 2` capped extraction at
 * ~13 items regardless of `--limit` (see #1471). This helper drives scrolls
 * dynamically:
 *
 *   - count visible `section.note-item` rows (excluding related-search
 *     `.query-note-item` rows)
 *   - if count >= targetCount → break (got enough)
 *   - if two consecutive scrolls add no new rows → break (DOM plateaued,
 *     no more lazy-load available)
 *   - hard cap at `maxScrolls` iterations (default 15) to bound runtime
 *
 * Exported so the rednote adapter (same DOM shape) can reuse it.
 */
export function buildScrollUntilJs(targetCount, maxScrolls = 15) {
    if (!Number.isSafeInteger(targetCount) || targetCount < 1) {
        throw new ArgumentError(`targetCount must be a positive integer, got ${JSON.stringify(targetCount)}`);
    }
    if (!Number.isSafeInteger(maxScrolls) || maxScrolls < 1) {
        throw new ArgumentError(`maxScrolls must be a positive integer, got ${JSON.stringify(maxScrolls)}`);
    }
    return `
      (async () => {
        const isVisibleNote = (el) => {
          if (el.classList.contains('query-note-item')) return false;
          const rect = el.getBoundingClientRect();
          if (rect.width <= 0 || rect.height <= 0) return false;
          const style = getComputedStyle(el);
          return style.display !== 'none' && style.visibility !== 'hidden';
        };
        // Note containers: legacy \`section.note-item\` first, fallback to
        // any \`<section>\` that wraps a search-result/explore note link
        // (#1506 reports the class being dropped on some xhs renders).
        const collectNoteCards = () => {
          const classMatches = document.querySelectorAll('section.note-item');
          if (classMatches.length > 0) return classMatches;
          const sections = new Set();
          for (const a of document.querySelectorAll('a[href*="/search_result/"], a[href*="/explore/"]')) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Omit the second argument to use the safe default of 15.
  2. Pass a positive safe integer, e.g. buildScrollUntilJs(50, 30) for more scrolls.
  3. Validate/parse config values before passing: Number.isSafeInteger(cfg.maxScrolls).
  4. Clamp with Math.max(1, Math.trunc(n)) when deriving the cap dynamically.

Example fix

// before
buildScrollUntilJs(limit, config.scrollMax);
// after
const maxScrolls = Number.isSafeInteger(config.scrollMax) && config.scrollMax >= 1
  ? config.scrollMax : 15;
buildScrollUntilJs(limit, maxScrolls);
Defensive patterns

Strategy: validation

Validate before calling

function assertMaxScrolls(v) {
  if (!Number.isSafeInteger(v) || v < 1) {
    throw new TypeError(`maxScrolls must be a positive safe integer, got ${JSON.stringify(v)}`);
  }
}
assertMaxScrolls(maxScrolls ?? 15);

Type guard

const isValidMaxScrolls = (v) => Number.isSafeInteger(v) && v >= 1;

Prevention

When it happens

Trigger: Calling buildScrollUntilJs(targetCount, maxScrolls) with 0, a negative value, undefined, NaN, Infinity, or a float as the second argument.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/65077822dfb5faf6. Report an issue: GitHub.