{"record":{"id":"7766971b69ff7a11","repo":"jackwener/OpenCLI","slug":"linkedin-messengermessages-payload-contains-a-mess","errorCode":null,"errorMessage":"LinkedIn messengerMessages payload contains a message without an id.","messagePattern":"LinkedIn messengerMessages payload contains a message without an id\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/thread-snapshot.js","lineNumber":269,"sourceCode":"  }\n\n  const participants = Array.from(entities.values()).filter(\n    (entity) => entity.$type === 'com.linkedin.messenger.MessagingParticipant',\n  );\n  const recipientNames = Array.from(new Set(participants\n    .filter((participant) => participant.hostIdentityUrn !== ownerUrn)\n    .map(participantName)\n    .filter(Boolean)));\n  if (recipientNames.length === 0) {\n    throw new CommandExecutionError('LinkedIn messengerMessages payload is missing the counterparty participant.');\n  }\n\n  const byMessageId = new Map();\n  for (const entity of entities.values()) {\n    if (entity.$type !== 'com.linkedin.messenger.Message') continue;\n    const messageId = normalizeWhitespace(entity.entityUrn || entity.backendUrn || entity.originToken);\n    if (!messageId) {\n      throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message without an id.');\n    }\n    const deliveredAt = Number(entity.deliveredAt);\n    if (!Number.isFinite(deliveredAt) || deliveredAt <= 0) {\n      throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message without a valid timestamp.');\n    }\n    const senderUrn = normalizeWhitespace(entity['*sender'] || entity['*actor']);\n    const sender = senderUrn ? entities.get(senderUrn) : null;\n    const speaker = participantName(sender);\n    if (!speaker) {\n      throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message with an unresolved sender.');\n    }\n    byMessageId.set(messageId, {\n      messageUrn: messageId,\n      speaker,\n      text: messageText(entity),\n      deliveredAt,\n    });\n  }","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/thread-snapshot.js#L251-L287","documentation":"For every com.linkedin.messenger.Message entity, parseThreadPages derives an id from entityUrn || backendUrn || originToken. It throws when all three are empty/whitespace, because messages must be keyed by a stable id for deduplication and sorting.","triggerScenarios":"A Message entity in `included` has none of entityUrn, backendUrn, or originToken set — e.g. LinkedIn schema change renaming the id field, or a partially hydrated entity from a truncated page.","commonSituations":"Outdated parser after a LinkedIn GraphQL schema update; stub/placeholder entities returned in some response variants; manually recorded fixtures missing id fields.","solutions":["Update the library to match the current Message entity id field names","Re-capture the thread — partial hydration is often transient","Log and inspect the offending entity's keys to find the new id field, and extend the fallback chain","If building fixtures, ensure every Message entity carries at least one of entityUrn/backendUrn/originToken"],"exampleFix":"// before\nconst messageId = normalizeWhitespace(entity.entityUrn || entity.backendUrn || entity.originToken);\n// after: extend fallback to new schema field\nconst messageId = normalizeWhitespace(entity.entityUrn || entity.backendUrn || entity.originToken || entity.messageUrn);","handlingStrategy":"type-guard","validationCode":"function messageHasId(entity) {\n  return !!(entity.entityUrn || entity.backendUrn || entity.originToken);\n}\nconst bad = payloads.flatMap(p => p.included).filter(e => e?.$type === 'com.linkedin.messenger.Message' && !messageHasId(e));\nif (bad.length) throw new Error('Message entities missing ids: ' + bad.length);","typeGuard":"const hasMessageId = (e) => typeof (e.entityUrn || e.backendUrn || e.originToken) === 'string' && !!(e.entityUrn || e.backendUrn || e.originToken).trim();","tryCatchPattern":"try {\n  const snap = await run('linkedin thread-snapshot', { 'thread-url': url });\n} catch (err) {\n  if (String(err.message).includes('message without an id')) {\n    // schema drift: log entity keys and upgrade parser\n  } else throw err;\n}","preventionTips":["Inspect captured Message entities for id fields after LinkedIn schema updates","Extend the id fallback chain when new id fields appear","Validate fixtures contain entityUrn/backendUrn/originToken before replay"],"tags":["linkedin","missing-identifier","payload-validation"],"backgroundTag":"missing-required-field","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}