{"record":{"id":"c1864056480ece0e","repo":"jackwener/OpenCLI","slug":"sales-navigator-messaging-threads-api-returned-mal-c18640","errorCode":null,"errorMessage":"Sales Navigator messaging threads API returned malformed thread row","messagePattern":"Sales Navigator messaging threads API returned malformed thread row","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-inbox.js","lineNumber":65,"sourceCode":"\nfunction isSelfParticipant(profile) {\n  const degree = String(profile?.degree ?? '').trim();\n  return degree === '0';\n}\n\nfunction otherParticipantName(thread) {\n  const participants = getThreadParticipants(thread);\n  const other = participants.find((p) => !isSelfParticipant(p)) || participants[0];\n  return normalizeWhitespace(other?.fullName || [other?.firstName, other?.lastName].filter(Boolean).join(' '));\n}\n\nfunction parseSalesnavThreads(json) {\n  if (!json || typeof json !== 'object' || !Array.isArray(json.elements)) {\n    throw new CommandExecutionError('Sales Navigator messaging threads API returned malformed payload');\n  }\n  return json.elements.map((thread) => {\n    if (!thread || typeof thread !== 'object') {\n      throw new CommandExecutionError('Sales Navigator messaging threads API returned malformed thread row');\n    }\n    const messages = Array.isArray(thread?.messages) ? thread.messages : [];\n    const lastMessage = messages[0] || {};\n    const deliveredAt = Number(lastMessage.deliveredAt || thread?.nextPageStartsAt || 0);\n    const threadId = normalizeWhitespace(thread?.id || '');\n    if (!threadId) {\n      throw new CommandExecutionError('Sales Navigator messaging thread row missing id');\n    }\n    return {\n      thread_id: threadId,\n      thread_url: salesnavThreadUrl(threadId),\n      person_name: otherParticipantName(thread),\n      last_message_snippet: normalizeWhitespace(lastMessage.body || lastMessage.subject || '').slice(0, 300),\n      last_activity_time: deliveredAt ? new Date(deliveredAt).toISOString() : '',\n      unread: Number(thread?.unreadMessageCount || 0) > 0,\n      unread_count: Number(thread?.unreadMessageCount || 0),\n      total_message_count: Number(thread?.totalMessageCount || messages.length || 0),\n      archived: Boolean(thread?.archived),","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-inbox.js#L47-L83","documentation":"This CommandExecutionError is thrown by parseSalesnavThreads when an individual thread inside json.elements is null or not an object. The overall payload shape was valid, but one row is unusable and would otherwise cause a downstream TypeError.","triggerScenarios":"The Sales Nav threads response contains null entries or non-object values inside the elements array — typically from partial/corrupt API responses or schema drift introducing wrapper objects where plain thread objects used to be.","commonSituations":"Sales Navigator pagination returning truncated/mixed pages; API version change wrapping thread data in an extra layer (so the element is no longer the thread itself); concurrent account access producing malformed entries.","solutions":["Update the library to handle the current thread element shape","Filter out non-object elements before parsing: elements.filter(t => t && typeof t === 'object')","Inspect the raw response to see what the malformed entries look like","Retry the request in case of a transient partial response"],"exampleFix":"// before\nparseSalesnavThreads(json); // throws on any bad row\n// after\njson.elements = json.elements.filter((t) => t && typeof t === 'object');\nparseSalesnavThreads(json);","handlingStrategy":"validation","validationCode":"const goodThreads = (json.elements || []).filter((t) => t && typeof t === 'object' && !Array.isArray(t));","typeGuard":"function isThreadRow(t) {\n  return !!t && typeof t === 'object' && !Array.isArray(t);\n}","tryCatchPattern":"try {\n  const rows = await fetchInboxRows(page);\n} catch (e) {\n  if (String(e.message).includes('malformed thread row')) {\n    logger.warn('Skipping bad thread row', { error: e.message });\n    return [];\n  }\n  throw e;\n}","preventionTips":["Sanitize elements arrays before parsing","Log one sample raw thread element when parsing fails","Keep the library current for Sales Nav schema drift","Use smaller page sizes to reduce partial responses"],"tags":["linkedin","sales-navigator","malformed-response"],"backgroundTag":"unexpected-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}