jackwener/OpenCLI · error · CommandExecutionError

Sales Navigator messaging threads API returned the same curs

Error message

Sales Navigator messaging threads API returned the same cursor twice

What it means

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.

Source

Thrown at clis/linkedin/salesnav-inbox.js:155

  let pagesFetched = 0;
  let hasMorePages = false;
  while (rows.length < limit && pagesFetched < maxPages) {
    const json = await fetchSalesnavJson(page, csrf, threadListUrl({ count: PAGE_SIZE, pageStartsAt }), 'Sales Navigator messaging threads API');
    pagesFetched += 1;
    const pageRows = parseSalesnavThreads(json);
    if (pageRows.length === 0) break;
    for (const row of pageRows) {
      if (seen.has(row.thread_id)) continue;
      seen.add(row.thread_id);
      rows.push(row);
      if (rows.length >= limit) break;
    }
    const last = pageRows[pageRows.length - 1];
    const next = last?.next_page_starts_at;
    hasMorePages = Boolean(next);
    if (!next) break;
    if (next === pageStartsAt) {
      throw new CommandExecutionError('Sales Navigator messaging threads API returned the same cursor twice');
    }
    pageStartsAt = next;
  }
  if (rows.length < limit && hasMorePages && pagesFetched >= maxPages) {
    throw new CommandExecutionError(`Sales Navigator messaging threads API reached the ${maxPages}-page safety cap before collecting ${limit} conversations`);
  }
  return rows.slice(0, limit).map((row, index) => ({ ...row, rank: index + 1 }));
}

export { THREAD_DECORATION, THREADS_BASE };

cli({
  site: 'linkedin',
  name: 'salesnav-inbox',
  access: 'read',
  description: 'List LinkedIn Sales Navigator message conversations with API pagination',
  domain: LINKEDIN_DOMAIN,
  strategy: Strategy.UI,

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command; a transient API pagination glitch often resolves on retry.
  2. Clear/refresh the browser session or log out and back into Sales Navigator to reset server-side pagination state.
  3. Reduce --limit or --max-pages so fewer cursor hops are needed.
  4. Add a small wait/delay between page fetches and retry; if it persists, report the inbox state to LinkedIn support.

Example fix

// before
await fetchInboxRows(page, { limit: 500, maxPages: 30 });
// after
// retry with smaller page budget and delay between pages
await fetchInboxRows(page, { limit: 100, maxPages: 30 });
Defensive patterns

Strategy: retry

Validate before calling

// inspect the last row's cursor before relying on pagination
const next = rows.at(-1)?.next_page_starts_at;
if (next && next === currentCursor) console.warn('cursor stuck; stop paginating');

Type guard

function hasNextCursor(row) { return typeof row?.next_page_starts_at === 'string' && row.next_page_starts_at.length > 0; }

Try / catch

try { await fetchInboxRows(page, { limit, maxPages }); } catch (e) { if (/same cursor twice/.test(e.message)) { await page.wait(3); /* retry once */ } else throw e; }

Prevention

When it happens

Trigger: 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).

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

Related errors


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