jackwener/OpenCLI · error · CommandExecutionError

Sales Navigator messaging thread row missing id

Error message

Sales Navigator messaging thread row missing id

What it means

This CommandExecutionError is thrown by parseSalesnavThreads when a thread row parses successfully but has no usable id (thread?.id is missing or whitespace). Thread IDs are required to build thread URLs and paginate, so rows without one are rejected rather than emitted with broken links.

Source

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

  const participants = getThreadParticipants(thread);
  const other = participants.find((p) => !isSelfParticipant(p)) || participants[0];
  return normalizeWhitespace(other?.fullName || [other?.firstName, other?.lastName].filter(Boolean).join(' '));
}

function parseSalesnavThreads(json) {
  if (!json || typeof json !== 'object' || !Array.isArray(json.elements)) {
    throw new CommandExecutionError('Sales Navigator messaging threads API returned malformed payload');
  }
  return json.elements.map((thread) => {
    if (!thread || typeof thread !== 'object') {
      throw new CommandExecutionError('Sales Navigator messaging threads API returned malformed thread row');
    }
    const messages = Array.isArray(thread?.messages) ? thread.messages : [];
    const lastMessage = messages[0] || {};
    const deliveredAt = Number(lastMessage.deliveredAt || thread?.nextPageStartsAt || 0);
    const threadId = normalizeWhitespace(thread?.id || '');
    if (!threadId) {
      throw new CommandExecutionError('Sales Navigator messaging thread row missing id');
    }
    return {
      thread_id: threadId,
      thread_url: salesnavThreadUrl(threadId),
      person_name: otherParticipantName(thread),
      last_message_snippet: normalizeWhitespace(lastMessage.body || lastMessage.subject || '').slice(0, 300),
      last_activity_time: deliveredAt ? new Date(deliveredAt).toISOString() : '',
      unread: Number(thread?.unreadMessageCount || 0) > 0,
      unread_count: Number(thread?.unreadMessageCount || 0),
      total_message_count: Number(thread?.totalMessageCount || messages.length || 0),
      archived: Boolean(thread?.archived),
      next_page_starts_at: normalizeWhitespace(thread?.nextPageStartsAt || ''),
      participants: getThreadParticipants(thread).map((p) => ({
        name: normalizeWhitespace(p.fullName || [p.firstName, p.lastName].filter(Boolean).join(' ')),
        entity_urn: normalizeWhitespace(p.entityUrn || ''),
        object_urn: normalizeWhitespace(p.objectUrn || ''),
        degree: normalizeWhitespace(p.degree ?? ''),
      })),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Update the library to read the id from its new location if the schema changed
  2. Skip threads without ids instead of failing the whole batch (filter before mapping)
  3. Inspect the raw payload to find where the thread identifier now lives
  4. Retry with a smaller page size to avoid partially hydrated rows

Example fix

// before
if (!threadId) throw new CommandExecutionError('...missing id');
// after
if (!threadId) return null; // caller: rows.filter(Boolean)
const threadId = normalizeWhitespace(thread?.id || thread?.entity?.id || '');
Defensive patterns

Strategy: validation

Validate before calling

const usable = (json.elements || []).filter((t) => t && typeof t === 'object' && String(t.id || '').trim() !== '');

Type guard

function hasThreadId(t) {
  return !!t && typeof t === 'object' && typeof t.id === 'string' && t.id.trim() !== '';
}

Try / catch

try {
  const rows = await fetchInboxRows(page);
} catch (e) {
  if (String(e.message).includes('missing id')) {
    logger.warn('Thread without id encountered; check Sales Nav schema');
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: A valid thread object in json.elements lacks the id field — schema change moving the id to a nested location (e.g. entity object), or a placeholder/empty thread returned by the API.

Common situations: Sales Navigator API version updates renaming the id field; thread rows representing system/special threads without stable ids; partially hydrated responses from aggressive pagination.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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