jackwener/OpenCLI · error · CommandExecutionError

Sales Navigator messaging thread API returned malformed mess

Error message

Sales Navigator messaging thread API returned malformed message row

What it means

Thrown inside the messages.map callback of parseSalesnavThreadMessages (clis/linkedin/salesnav-thread.js:100) when any element of thread.messages is null or not an object. Each message must be an object carrying id, author, body/deliveredAt fields to build an output row. The library fails loudly on the first bad row instead of emitting rows with undefined fields, so one corrupt element aborts the whole parse.

Source

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

  return byUrn;
}

function parseSalesnavThreadMessages(thread) {
  if (!thread || typeof thread !== 'object') {
    throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed payload');
  }
  const threadId = normalizeWhitespace(thread?.id || '');
  if (!threadId) {
    throw new CommandExecutionError('Sales Navigator messaging thread API returned a thread without id');
  }
  if (!Array.isArray(thread?.messages)) {
    throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed messages');
  }
  const byUrn = participantIndex(thread);
  const messages = thread.messages;
  const rows = messages.map((message) => {
    if (!message || typeof message !== 'object') {
      throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed message row');
    }
    const deliveredAt = Number(message?.deliveredAt || 0);
    const senderProfile = byUrn.get(message?.author);
    return {
      message_id: normalizeWhitespace(message?.id || ''),
      thread_id: threadId,
      sender: participantName(senderProfile) || normalizeWhitespace(message?.author || ''),
      sender_urn: normalizeWhitespace(message?.author || ''),
      text: normalizeWhitespace(message?.body || message?.systemMessageContent || ''),
      subject: normalizeWhitespace(message?.subject || ''),
      timestamp: deliveredAt ? new Date(deliveredAt).toISOString() : '',
      delivered_at: deliveredAt || '',
      type: normalizeWhitespace(message?.type || ''),
    };
  }).filter((row) => row.text || row.subject || row.message_id);
  rows.sort((a, b) => Number(a.delivered_at || 0) - Number(b.delivered_at || 0));
  return rows.map((row, index) => ({ index, ...row }));
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command to see if the null row is transient; then compare which thread/message triggers it (the index before failure localizes the corrupt message).
  2. Filter non-object message entries before parsing: thread.messages.filter((m) => m && typeof m === 'object').
  3. Refresh THREAD_DECORATION from a live network capture if message fields are being decoded into wrong types.
  4. If a specific thread always fails (e.g. contains deleted messages), skip that thread or report it upstream so the parser can tolerate null rows.

Example fix

// before (one null row aborts everything)
const messages = parseSalesnavThreadMessages(thread);
// after (drop non-object rows first)
const cleaned = { ...thread, messages: (thread.messages || []).filter((m) => m && typeof m === 'object') };
const messages = parseSalesnavThreadMessages(cleaned);
Defensive patterns

Strategy: type-guard

Validate before calling

// Sanitize message rows before parsing:
const safeThread = {
  ...thread,
  messages: (thread.messages || []).filter((m) => m && typeof m === 'object'),
};

Type guard

function isMessageObject(m) {
  return m !== null && typeof m === 'object' && !Array.isArray(m);
}

Try / catch

try {
  const messages = parseSalesnavThreadMessages(thread);
} catch (err) {
  if (/malformed message row/.test(err.message)) {
    // drop non-object rows (deleted/redacted messages serialize as null) and retry
    const cleaned = { ...thread, messages: thread.messages.filter(isMessageObject) };
    return parseSalesnavThreadMessages(cleaned);
  }
  throw err;
}

Prevention

When it happens

Trigger: The API returned an array containing null placeholders (e.g. deleted/redacted messages serialized as null); a decoration mismatch causing message entries to be decoded as strings or arrays; pagination stitching in fetchThreadWithPagination concatenating heterogeneous payloads where one page's element is not an object.

Common situations: Threads containing deleted or system-redacted messages that LinkedIn serializes as null entries; mixed-version decorations after a Sales Navigator redeploy; custom code merging thread pages before parsing and injecting undefined entries.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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