{"record":{"id":"2a840b6a33ad446e","repo":"jackwener/OpenCLI","slug":"xueqiu-comments-pagination-stopped-after-request","errorCode":null,"errorMessage":"xueqiu comments pagination stopped after request ${requestNumber}, collected ${collected}/${target} items, reason: ${reason}","messagePattern":"xueqiu comments pagination stopped after request (.+?), collected (.+?)/(.+?) items, reason: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"clis/xueqiu/comments.js","lineNumber":257,"sourceCode":" *\n * @param options Pagination inputs and a page-fetch callback.\n * @returns Deduplicated normalized rows, possibly partial with a warning.\n */\nexport async function collectCommentRows(options) {\n    const warn = options.warn ?? log.warn;\n    let rows = [];\n    const seenIds = new Set();\n    for (let requestNumber = 1; requestNumber <= options.maxRequests; requestNumber += 1) {\n        const response = await options.fetchPage(requestNumber, options.pageSize);\n        const classified = classifyXueqiuCommentsResponse(response);\n        if (requestNumber === 1 && classified.kind !== 'unknown') {\n            throwFirstPageFailure(classified.kind, options.symbol);\n        }\n        else if (classified.kind === 'empty') {\n            break;\n        }\n        else if (classified.kind !== 'unknown') {\n            warn(buildPaginationStopMessage(requestNumber, rows.length, options.limit, describeFailureKind(classified.kind)));\n            break;\n        }\n        const rawItems = getCommentItems(response.json);\n        const pageRows = rawItems\n            .map(item => normalizeCommentItem(item))\n            .filter(row => row.id);\n        if (pageRows.length === 0) {\n            if (requestNumber === 1) {\n                throw new CommandExecutionError(`Unexpected response while loading xueqiu comments for ${options.symbol}`, 'Run the command again with --verbose to inspect the raw site response.');\n            }\n            if (classified.kind === 'unknown') {\n                warn(buildPaginationStopMessage(requestNumber, rows.length, options.limit, describeFailureKind(classified.kind)));\n            }\n            break;\n        }\n        let advanced = false;\n        for (const row of pageRows) {\n            if (seenIds.has(row.id))","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xueqiu/comments.js#L239-L275","documentation":"During xueqiu comment collection, pagination stopped early because a page request was classified as a recoverable failure (not 'empty', not 'unknown'). The command warns with the request number, items collected vs target, and the classified reason, then returns what it has instead of throwing (first-page failures still throw).","triggerScenarios":"collectCommentRows loops requesting comment pages; a non-first page's classify() result is a known failure kind (e.g., rate-limit, auth-wall, server error), triggering warn(buildPaginationStopMessage(...)) and a break.","commonSituations":"Xueqiu rate limiting or temporary 4xx/5xx on comments API after several pages, symbol requiring login for older comments, token/cookie expiry mid-pagination, requesting very large limits (target far beyond free pages).","solutions":["Read `reason` in the message and address it (e.g., wait out rate limit, refresh login cookies)","Lower --limit so the target is reachable before the failure point","Rerun the command later to collect remaining comments, or retry with backoff","Ensure a valid authenticated Xueqiu session if the reason indicates an auth wall","Check the symbol id is valid — partially valid symbols sometimes serve only the first pages"],"exampleFix":"// before: limit too high, stopped at rate limit after 3 pages (60/500)\nopencli xueqiu comments --symbol SH600000 --limit 500\n// after: smaller limit + valid session\nopencli xueqiu comments --symbol SH600000 --limit 50","handlingStrategy":"retry","validationCode":"// Pre-flight: ensure a live session and a sane limit\nif (!await hasValidXueqiuSession()) throw new Error('Login required');\nif (options.limit > 200) console.warn('Large limits often stop early due to rate limits');","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await xueqiuComments(symbol, { limit });\n  if (rows.length < limit) {\n    console.warn(`Partial: ${rows.length}/${limit}`); // parse the stop reason from stderr\n  }\n} catch (err) {\n  // first-page failures throw; back off and retry later\n}","preventionTips":["Keep per-run limits modest and paginate in separate runs","Back off on rate-limit reasons before retrying","Maintain a fresh authenticated Xueqiu session/cookies","Parse the `reason:` field from the warning to choose between wait, re-auth, or reduce-limit"],"tags":["pagination","rate-limit","xueqiu","partial-results","api"],"backgroundTag":"pagination-stopped-early","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}