{"record":{"id":"9463da333612f2d7","repo":"jackwener/OpenCLI","slug":"sales-navigator-messaging-threads-api-returned-mal","errorCode":null,"errorMessage":"Sales Navigator messaging threads API returned malformed payload","messagePattern":"Sales Navigator messaging threads API returned malformed payload","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-inbox.js","lineNumber":61,"sourceCode":"  const resolution = thread?.participantsResolutionResults || {};\n  const participants = Array.isArray(thread?.participants) ? thread.participants : Object.keys(resolution);\n  return participants.map((urn) => resolution[urn] || { entityUrn: urn }).filter(Boolean);\n}\n\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() : '',","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-inbox.js#L43-L79","documentation":"This CommandExecutionError is thrown by parseSalesnavThreads when the Sales Navigator messaging threads API response is not an object with an elements array. It guards against HTML error pages, auth redirects, or schema changes reaching the row parser.","triggerScenarios":"Calling fetchInboxRows/pageRows when the Sales Nav messaging endpoint returns JSON lacking json.elements — e.g. a login redirect body, a rate-limit JSON error, or a LinkedIn API schema change renaming/moving elements.","commonSituations":"Expired Sales Navigator session returning a non-thread payload; CSRF token stale so the API answers with an error envelope; LinkedIn changed the messaging threads response shape; hitting the wrong endpoint URL.","solutions":["Re-authenticate / refresh the LinkedIn session and retry","Verify the CSRF token is fresh (getCsrf) before calling the API","Update the library if Sales Navigator changed the threads response schema","Log the raw response body to confirm what the endpoint actually returned"],"exampleFix":"// before\nconst json = JSON.parse(rawText);\nparseSalesnavThreads(json);\n// after\nconst json = JSON.parse(rawText);\nif (!json?.elements) throw new Error('threads payload missing elements: ' + rawText.slice(0, 200));\nparseSalesnavThreads(json);","handlingStrategy":"validation","validationCode":"const json = await fetchPageJson(url);\nif (!json || typeof json !== 'object' || !Array.isArray(json.elements)) {\n  throw new Error('Threads payload malformed; raw: ' + JSON.stringify(json).slice(0, 200));\n}","typeGuard":"function isThreadsPayload(v) {\n  return !!v && typeof v === 'object' && Array.isArray(v.elements);\n}","tryCatchPattern":"try {\n  const rows = await fetchInboxRows(page);\n} catch (e) {\n  if (String(e.message).includes('malformed payload')) {\n    await relogin(page); // often a stale session\n    return fetchInboxRows(page);\n  }\n  throw e;\n}","preventionTips":["Refresh the CSRF token before each inbox fetch","Capture raw response bodies on failure for diagnosis","Pin/monitor Sales Nav API schema changes","Retry once on malformed payloads before failing the job"],"tags":["linkedin","sales-navigator","api","malformed-response"],"backgroundTag":"unexpected-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}