jackwener/OpenCLI · error · CommandExecutionError

Sales Navigator messaging thread API returned partial histor

Error message

Sales Navigator messaging thread API returned partial history (${have}/${total})

What it means

fetchThreadWithPagination keeps requesting older message pages until the requested count reaches the limit. After fetching it compares LinkedIn's reported totalMessageCount with the number of messages actually collected; if fewer were returned than both the total and the requested limit, it throws CommandExecutionError about partial history. The library refuses to silently return an incomplete conversation.

Source

Thrown at clis/linkedin/salesnav-thread.js:165

async function fetchThreadWithPagination(page, csrf, threadId, limit = DEFAULT_MESSAGE_LIMIT) {
  let requested = THREAD_PAGE_SIZE;
  if (limit < requested) requested = limit;
  let thread = null;
  for (let attempts = 0; attempts < 30; attempts += 1) {
    thread = await fetchSalesnavJson(page, csrf, threadApiUrl(threadId, requested), 'Sales Navigator messaging thread API');
    const total = Number(thread?.totalMessageCount || 0);
    const have = Array.isArray(thread?.messages) ? thread.messages.length : 0;
    if (have >= limit || (total && have >= total) || requested >= limit) break;
    let nextRequested = requested + THREAD_PAGE_SIZE;
    if (total && total > nextRequested) nextRequested = total;
    if (nextRequested > limit) nextRequested = limit;
    requested = nextRequested;
  }
  const total = Number(thread?.totalMessageCount || 0);
  const have = Array.isArray(thread?.messages) ? thread.messages.length : 0;
  if (total && have < total && have < limit) {
    throw new CommandExecutionError(`Sales Navigator messaging thread API returned partial history (${have}/${total})`);
  }
  return thread;
}

cli({
  site: 'linkedin',
  name: 'salesnav-thread',
  access: 'read',
  description: 'Return full Sales Navigator message history for a thread id, Sales Navigator inbox URL, lead URL, recipient urn, or exact recipient name',
  domain: LINKEDIN_DOMAIN,
  strategy: Strategy.UI,
  browser: true,
  args: [
    { name: 'thread-or-recipient', type: 'string', required: true, positional: true, help: 'Sales Navigator inbox URL/thread id, Sales Navigator lead URL, recipient urn, or exact participant name' },
    { name: 'limit', type: 'number', default: DEFAULT_MESSAGE_LIMIT, help: 'Maximum messages to return (1-500)' },
    { name: 'max-pages', type: 'number', default: 30, help: 'Maximum inbox pages to scan when resolving a recipient' },
  ],
  columns: ['index', 'thread_id', 'thread_url', 'sender', 'text', 'timestamp', 'subject', 'message_id', 'sender_urn', 'delivered_at', 'type', 'total_message_count'],

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command; transient truncation often resolves on a second run.
  2. Raise --limit so have is no longer below the limit used in the check (the error only fires when have < limit).
  3. Inspect the thread in Sales Navigator to confirm messages were deleted/hidden; if so, accept the partial history by handling the error and using what was fetched on a later attempt with a smaller limit.
  4. Report/pin the LinkedIn API version if the thread API recently changed behavior.

Example fix

// before
thread = await fetchThreadWithPagination(page, csrf, threadId, 500) // partial -> throws
// after
try {
  thread = await fetchThreadWithPagination(page, csrf, threadId, 500)
} catch (e) {
  if (/partial history/.test(e.message)) {
    thread = await fetchThreadWithPagination(page, csrf, threadId, 50) // smaller limit avoids have<limit trigger
  } else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try { thread = await thread() } catch (e) { if (/partial history \(\d+\/\d+\)/.test(e.message)) { await sleep(1000); thread = await thread(); /* or accept partial with lower limit */ } else throw e; }

Prevention

When it happens

Trigger: LinkedIn's messaging thread API stops returning further pages before totalMessageCount is reached (e.g. an empty or truncated next-page cursor, deleted/hidden messages, or an API response that reports a larger total than it will actually serve), while have < the requested limit.

Common situations: Threads where LinkedIn has hidden or deleted some older messages so the real retrievable count is below totalMessageCount; transient API flakiness returning a final page early; very long threads where pagination ends prematurely after a LinkedIn messaging API change.

Related errors


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