jackwener/OpenCLI · error · CommandExecutionError
Sales Navigator messaging thread API returned a thread witho
Error message
Sales Navigator messaging thread API returned a thread without id
What it means
Thrown by parseSalesnavThreadMessages (clis/linkedin/salesnav-thread.js:91) when the thread object exists but its id field is missing or whitespace-only after normalization. The id is required to build thread URLs, correlate messages, and anchor each output row's thread_id. The library treats an id-less thread as a malformed response rather than silently returning rows without an identifier.
Source
Thrown at clis/linkedin/salesnav-thread.js:91
function participantIndex(thread) {
const resolution = thread?.participantsResolutionResults || {};
const participants = Array.isArray(thread?.participants) ? thread.participants : Object.keys(resolution);
const byUrn = new Map();
for (const urn of participants) {
const profile = resolution[urn] || { entityUrn: urn };
byUrn.set(urn, profile);
}
return byUrn;
}
function parseSalesnavThreadMessages(thread) {
if (!thread || typeof thread !== 'object') {
throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed payload');
}
const threadId = normalizeWhitespace(thread?.id || '');
if (!threadId) {
throw new CommandExecutionError('Sales Navigator messaging thread API returned a thread without id');
}
if (!Array.isArray(thread?.messages)) {
throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed messages');
}
const byUrn = participantIndex(thread);
const messages = thread.messages;
const rows = messages.map((message) => {
if (!message || typeof message !== 'object') {
throw new CommandExecutionError('Sales Navigator messaging thread API returned malformed message row');
}
const deliveredAt = Number(message?.deliveredAt || 0);
const senderProfile = byUrn.get(message?.author);
return {
message_id: normalizeWhitespace(message?.id || ''),
thread_id: threadId,
sender: participantName(senderProfile) || normalizeWhitespace(message?.author || ''),
sender_urn: normalizeWhitespace(message?.author || ''),
text: normalizeWhitespace(message?.body || message?.systemMessageContent || ''),View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the command to rule out a transient partial response.
- Verify the THREAD_DECORATION version still matches the live /sales-api/salesApiThreads response shape; refresh from the network tab if LinkedIn changed it.
- Ensure you are parsing the thread-detail response (which carries id), not an inbox-list row where the id may live in a different field like entityUrn.
- Log the raw thread object (Object.keys(thread)) before parsing to identify the actual id field name and adapt or report the mismatch.
Example fix
// before (parsing an inbox row that stores id elsewhere)
parseSalesnavThreadMessages(inboxRow);
// after (normalize the id before parsing)
const thread = { ...threadDetail, id: threadDetail.id || threadDetail.entityUrn?.split(':').pop() };
parseSalesnavThreadMessages(thread); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure an id exists before parsing:
const threadId = typeof thread?.id === 'string' ? thread.id.trim() : '';
if (!threadId) throw new Error('thread payload has no id; verify endpoint/decoration'); Type guard
function hasThreadId(t) {
return t !== null && typeof t === 'object' &&
typeof t.id === 'string' && t.id.trim().length > 0;
} Try / catch
try {
const messages = parseSalesnavThreadMessages(thread);
} catch (err) {
if (/thread without id/.test(err.message) && thread?.entityUrn) {
// normalize alternate id field, then retry the parse
return parseSalesnavThreadMessages({ ...thread, id: thread.entityUrn.split(':').pop() });
}
throw err;
} Prevention
- Parse only thread-detail responses with this function; inbox-list rows keep the id in a different field.
- Inspect Object.keys(thread) when this fires — a renamed id field signals a decoration version bump.
- Keep THREAD_DECORATION current with the live /sales-api/salesApiThreads response.
When it happens
Trigger: LinkedIn returned a thread-shaped object whose id was absent (decoration mismatch dropping the id field); fetchSalesnavJson parsed a partial/error payload that still looked like an object; passing a thread object fetched from a different endpoint that uses a different id key (e.g. entityUrn instead of id).
Common situations: A Sales Navigator redeploy renaming or relocating the id field under a new decoration version; reusing this parser on thread objects from a different API (inbox listing vs thread detail) whose id lives in another property; truncated responses due to session limits.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Sales Navigator lead row missing name
- Sales Navigator messaging thread API returned malformed payl
- Sales Navigator messaging thread API returned malformed mess
- Sales Navigator messaging thread API returned malformed mess
- Sales Navigator lead search API returned malformed payload
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/af18744531a179f7.
Report an issue: GitHub.