{"record":{"id":"03d946e7fccbe5e2","repo":"jackwener/OpenCLI","slug":"linkedin-messengermessages-payload-contains-a-mess-03d946","errorCode":null,"errorMessage":"LinkedIn messengerMessages payload contains a message without a valid timestamp.","messagePattern":"LinkedIn messengerMessages payload contains a message without a valid timestamp\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/thread-snapshot.js","lineNumber":273,"sourceCode":"  );\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  }\n\n  const messages = Array.from(byMessageId.values())\n    .sort((left, right) => left.deliveredAt - right.deliveredAt || left.messageUrn.localeCompare(right.messageUrn))\n    .map((message, index) => ({ index, ...message }));","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/thread-snapshot.js#L255-L291","documentation":"Each Message entity must have a positive finite deliveredAt epoch value; the parser throws when Number(entity.deliveredAt) is NaN, negative, or zero. Timestamps drive message ordering, so a missing or invalid one makes the conversation snapshot unreliable.","triggerScenarios":"A Message entity has deliveredAt missing, null, a non-numeric string, or 0 — typically schema drift, placeholder entities, or a message captured before its timestamp was populated (e.g. just-sent message in a live capture).","commonSituations":"Snapshotting a thread immediately after sending a message; outdated parser after LinkedIn renamed/retyped deliveredAt (e.g. moved to nested createdAt); fixtures with string timestamps or missing fields.","solutions":["Re-run the snapshot after a short delay so freshly sent messages get timestamps","Update the library to read the current timestamp field if LinkedIn renamed deliveredAt","Inspect the entity: if deliveredAt is a string epoch, extend the parser to coerce strings","Exclude or pre-filter malformed messages before calling the CLI if you control the capture pipeline"],"exampleFix":"// before\nconst deliveredAt = Number(entity.deliveredAt);\n// after: tolerate string epochs and alternate fields\nconst deliveredAt = Number(entity.deliveredAt ?? entity.createdAt ?? Date.parse(entity.deliveredAtDateTime) || NaN);","handlingStrategy":"validation","validationCode":"function hasValidTimestamp(entity) {\n  const t = Number(entity.deliveredAt);\n  return Number.isFinite(t) && t > 0;\n}\nif (!payload.included.filter(e => e?.$type === 'com.linkedin.messenger.Message').every(hasValidTimestamp)) {\n  throw new Error('Message missing deliveredAt; delay capture after sends');\n}","typeGuard":"const hasValidDeliveredAt = (e) => { const t = Number(e.deliveredAt); return Number.isFinite(t) && t > 0; };","tryCatchPattern":"try {\n  const snap = await run('linkedin thread-snapshot', { 'thread-url': url });\n} catch (err) {\n  if (String(err.message).includes('without a valid timestamp')) {\n    // retry after a short delay or pre-filter timestamp-less messages\n  } else throw err;\n}","preventionTips":["Wait a few seconds after sending a message before snapshotting","Coerce string epochs defensively in your own pre-parsing","Watch for deliveredAt renames in LinkedIn's GraphQL schema"],"tags":["linkedin","timestamp-validation","payload-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}