{"record":{"id":"4dde9ccd1fef5315","repo":"jackwener/OpenCLI","slug":"gmail-thread-pagination-page-pagenumber-repeate","errorCode":null,"errorMessage":"Gmail thread pagination page ${pageNumber} repeated an earlier response; refusing partial results","messagePattern":"Gmail thread pagination page (.+?) repeated an earlier response; refusing partial results","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/gmail/utils.js","lineNumber":537,"sourceCode":"        if (clicked !== true) break;\n      }\n    }\n    const bodies = await waitGmailCaptures(\n      page,\n      'bv',\n      pageNumber === 1 ? 'thread list' : `thread pagination page ${pageNumber}`,\n    );\n    const pageRows = bodies.flatMap(parseBatchView);\n    let added = 0;\n    for (const row of pageRows) {\n      if (seen.has(row.threadId)) continue;\n      seen.add(row.threadId);\n      rows.push(row);\n      added += 1;\n      if (rows.length >= limit) return rows;\n    }\n    if (pageNumber > 1 && added === 0) {\n      throw new CommandExecutionError(`Gmail thread pagination page ${pageNumber} repeated an earlier response; refusing partial results`);\n    }\n    if (pageRows.length < PAGE_SIZE) break;\n  }\n  if (rows.length === 0) {\n    throw new EmptyResultError('gmail search', `No threads matched \"${normalizedQuery}\"`);\n  }\n  return rows.slice(0, limit);\n}\n\nexport async function listLabels(page, account = 0) {\n  await ensureGmailReady(page, account, 'labels');\n  await installGmailCapture(page, account, 'bv', 'labels');\n  await submitSearch(page, 'in:anywhere', 'labels');\n  const bodies = await waitGmailCaptures(page, 'bv', 'labels');\n  const labels = bodies.flatMap(parseLabels);\n  const fallback = labels.length === 0 ? await renderedLabels(page, account) : [];\n  const unique = [...new Map([...labels, ...fallback].map((row) => [row.id, row])).values()];\n  if (unique.length === 0) throw new EmptyResultError('gmail labels', 'Gmail returned no labels');","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/gmail/utils.js#L519-L555","documentation":"During pagination, if a page after the first adds zero new thread IDs (all rows were already seen), the library assumes the UI stopped advancing pages and throws CommandExecutionError rather than returning silently truncated results. It protects callers from partial data disguised as a complete result.","triggerScenarios":"Querying with a limit larger than one PAGE_SIZE when Gmail's next-page navigation fails or re-renders the same results; repeated thread IDs across pages due to a stale page state or a flaky capture of the results list.","commonSituations":"Large limit values (limit >> PAGE_SIZE) on slow connections; Gmail UI changes breaking the pagination selector so every 'page' shows the same rows; very small result sets interacting with page-advance logic.","solutions":["Lower the limit so it fits within one page (PAGE_SIZE) of results","Retry the search once — the failure is often transient UI state","If persistent, the query may genuinely have fewer unique results than requested; reduce expectations or refine the query","Verify the library/browser setup matches a supported Gmail UI version"],"exampleFix":"// before\nconst rows = await queryThreads(page, 'label:unread', { limit: 200 });\n// after\nconst rows = await queryThreads(page, 'label:unread', { limit: 50 }); // fit within one page or retry on CommandExecutionError","handlingStrategy":"retry","validationCode":"const SAFE_LIMIT = 50; // one page\nconst limit = Math.min(requestedLimit, SAFE_LIMIT);","typeGuard":"const fitsOnePage = (n) => Number.isInteger(n) && n > 0 && n <= 50;","tryCatchPattern":"try { rows = await queryThreads(page, q, { limit }); }\ncatch (e) { if (e instanceof CommandExecutionError && /repeated an earlier response/.test(e.message)) rows = await queryThreads(page, q, { limit: 50 }); else throw e; }","preventionTips":["Keep limits within one page size when possible","Retry once on this specific CommandExecutionError","Refine queries to reduce reliance on deep pagination"],"tags":["pagination","gmail","ui-automation"],"backgroundTag":"pagination-stall-duplicate-page","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}