{"record":{"id":"e653514a5e93afbf","repo":"jackwener/OpenCLI","slug":"sales-navigator-messaging-threads-api-returned-the","errorCode":null,"errorMessage":"Sales Navigator messaging threads API returned the same cursor twice","messagePattern":"Sales Navigator messaging threads API returned the same cursor twice","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-inbox.js","lineNumber":155,"sourceCode":"  let pagesFetched = 0;\n  let hasMorePages = false;\n  while (rows.length < limit && pagesFetched < maxPages) {\n    const json = await fetchSalesnavJson(page, csrf, threadListUrl({ count: PAGE_SIZE, pageStartsAt }), 'Sales Navigator messaging threads API');\n    pagesFetched += 1;\n    const pageRows = parseSalesnavThreads(json);\n    if (pageRows.length === 0) break;\n    for (const row of pageRows) {\n      if (seen.has(row.thread_id)) continue;\n      seen.add(row.thread_id);\n      rows.push(row);\n      if (rows.length >= limit) break;\n    }\n    const last = pageRows[pageRows.length - 1];\n    const next = last?.next_page_starts_at;\n    hasMorePages = Boolean(next);\n    if (!next) break;\n    if (next === pageStartsAt) {\n      throw new CommandExecutionError('Sales Navigator messaging threads API returned the same cursor twice');\n    }\n    pageStartsAt = next;\n  }\n  if (rows.length < limit && hasMorePages && pagesFetched >= maxPages) {\n    throw new CommandExecutionError(`Sales Navigator messaging threads API reached the ${maxPages}-page safety cap before collecting ${limit} conversations`);\n  }\n  return rows.slice(0, limit).map((row, index) => ({ ...row, rank: index + 1 }));\n}\n\nexport { THREAD_DECORATION, THREADS_BASE };\n\ncli({\n  site: 'linkedin',\n  name: 'salesnav-inbox',\n  access: 'read',\n  description: 'List LinkedIn Sales Navigator message conversations with API pagination',\n  domain: LINKEDIN_DOMAIN,\n  strategy: Strategy.UI,","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-inbox.js#L137-L173","documentation":"fetchInboxRows paginates the Sales Navigator messaging threads API using next_page_starts_at cursors. If the API returns a cursor identical to the one just used, the loop would fetch the same page forever, so the library detects this and throws a CommandExecutionError instead.","triggerScenarios":"Sales Navigator's messaging threads endpoint returns a page whose last row's next_page_starts_at equals the cursor used for the current page (stale or duplicated pagination cursor).","commonSituations":"LinkedIn API-side pagination glitches or caching; very large or archived inboxes where the backend keeps repeating the same cursor; scraping a session where the inbox view did not actually advance.","solutions":["Re-run the command; a transient API pagination glitch often resolves on retry.","Clear/refresh the browser session or log out and back into Sales Navigator to reset server-side pagination state.","Reduce --limit or --max-pages so fewer cursor hops are needed.","Add a small wait/delay between page fetches and retry; if it persists, report the inbox state to LinkedIn support."],"exampleFix":"// before\nawait fetchInboxRows(page, { limit: 500, maxPages: 30 });\n// after\n// retry with smaller page budget and delay between pages\nawait fetchInboxRows(page, { limit: 100, maxPages: 30 });","handlingStrategy":"retry","validationCode":"// inspect the last row's cursor before relying on pagination\nconst next = rows.at(-1)?.next_page_starts_at;\nif (next && next === currentCursor) console.warn('cursor stuck; stop paginating');","typeGuard":"function hasNextCursor(row) { return typeof row?.next_page_starts_at === 'string' && row.next_page_starts_at.length > 0; }","tryCatchPattern":"try { await fetchInboxRows(page, { limit, maxPages }); } catch (e) { if (/same cursor twice/.test(e.message)) { await page.wait(3); /* retry once */ } else throw e; }","preventionTips":["Retry the command once on this error; it is often transient.","Keep max-pages and limit modest to reduce cursor hops.","Refresh the browser session if the error repeats.","Log each cursor to detect repetition early."],"tags":["pagination","sales-navigator","scraping"],"backgroundTag":"pagination-cursor-stuck","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}