jackwener/OpenCLI · error · CommandExecutionError
LinkedIn messengerMessages payload is missing the counterpar
Error message
LinkedIn messengerMessages payload is missing the counterparty participant.
What it means
parseThreadPages filters MessagingParticipant entities to those whose hostIdentityUrn differs from the owner URN, then derives display names. It throws when no counterparty (non-owner) participant yields a name, meaning the payload has no usable other-side participant entity — so the snapshot would have no 'recipient'.
Source
Thrown at clis/linkedin/thread-snapshot.js:261
}
}
apiUrls.push(page.url);
}
const ownerUrn = ownerUrnFromApiUrls(apiUrls);
if (!ownerUrn) {
throw new CommandExecutionError('LinkedIn messengerMessages URL is missing the conversation owner identity.');
}
const participants = Array.from(entities.values()).filter(
(entity) => entity.$type === 'com.linkedin.messenger.MessagingParticipant',
);
const recipientNames = Array.from(new Set(participants
.filter((participant) => participant.hostIdentityUrn !== ownerUrn)
.map(participantName)
.filter(Boolean)));
if (recipientNames.length === 0) {
throw new CommandExecutionError('LinkedIn messengerMessages payload is missing the counterparty participant.');
}
const byMessageId = new Map();
for (const entity of entities.values()) {
if (entity.$type !== 'com.linkedin.messenger.Message') continue;
const messageId = normalizeWhitespace(entity.entityUrn || entity.backendUrn || entity.originToken);
if (!messageId) {
throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message without an id.');
}
const deliveredAt = Number(entity.deliveredAt);
if (!Number.isFinite(deliveredAt) || deliveredAt <= 0) {
throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message without a valid timestamp.');
}
const senderUrn = normalizeWhitespace(entity['*sender'] || entity['*actor']);
const sender = senderUrn ? entities.get(senderUrn) : null;
const speaker = participantName(sender);
if (!speaker) {
throw new CommandExecutionError('LinkedIn messengerMessages payload contains a message with an unresolved sender.');View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the snapshot to capture a complete payload including participant entities
- Update the library — participantName may need to handle new name field layouts for MessagingParticipant
- Test with a normal thread with an active counterparty to confirm the tool works; threads to self/deactivated accounts are unsupported
- If capturing manually, include all pages so participant entities in later pages aren't lost
Example fix
// before: participant entity lacking name fields
{ "$type": "com.linkedin.messenger.MessagingParticipant", "hostIdentityUrn": "urn:li:person:abc" }
// after: complete capture including the linked miniProfile name fields
{ "$type": "com.linkedin.messenger.MessagingParticipant", "hostIdentityUrn": "urn:li:person:abc", "name": { "firstName": "Jane", "lastName": "Doe" } } Defensive patterns
Strategy: validation
Validate before calling
function hasCounterparty(payload, ownerUrn) {
return payload.included.some(e => e?.$type === 'com.linkedin.messenger.MessagingParticipant'
&& e.hostIdentityUrn !== ownerUrn);
}
if (!pages.every(p => hasCounterparty(p.json, myOwnerUrn))) throw new Error('No counterparty participant captured'); Type guard
const isParticipant = (e) => !!e && typeof e === 'object' && e.$type === 'com.linkedin.messenger.MessagingParticipant' && typeof e.hostIdentityUrn === 'string';
Try / catch
try {
const snap = await run('linkedin thread-snapshot', { 'thread-url': url });
} catch (err) {
if (String(err.message).includes('missing the counterparty participant')) {
// handle self-threads or restricted profiles gracefully
} else throw err;
} Prevention
- Skip threads with no active counterparty (self-messages, deactivated accounts)
- Capture all pages so participant entities aren't dropped
- Keep participantName parsing updated for name field layout changes
When it happens
Trigger: All MessagingParticipant entities match the owner URN, participantName() returns empty for the counterparty entities, or the counterparty participant entity is absent from `included` (e.g. restricted/deactivated profile or incomplete included array).
Common situations: Messaging a restricted or deleted account; LinkedIn omitting participant entities for privacy; a truncated page capture that missed participant entities; self-directed threads (message to self) that genuinely have no counterparty.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- LinkedIn people search returned malformed extraction payload
- LinkedIn cookie lookup returned malformed payload
- LinkedIn people search returned malformed extraction payload
- LinkedIn sent invitations returned a malformed extraction pa
- LinkedIn services-read returned malformed extraction payload
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/4707911728cac9e7.
Report an issue: GitHub.