jackwener/OpenCLI · error · CommandExecutionError
Sales Navigator lead row missing profile identity
Error message
Sales Navigator lead row missing profile identity
What it means
After extracting the name, parseLeads derives the profile URL from el.entityUrn via profileUrlFromEntityUrn (regex fs_salesProfile:(<token>)) and throws CommandExecutionError if no /in/<token> URL can be built. The profile identity is mandatory because downstream commands (e.g. connect) key on it and the output dedupes on profile_url.
Source
Thrown at clis/linkedin/salesnav-search.js:94
function parseLeads(json) {
if (!json || typeof json !== 'object' || !Array.isArray(json.elements)) {
throw new CommandExecutionError('Sales Navigator lead search API returned malformed payload');
}
const leads = [];
for (const el of json.elements) {
if (!el || typeof el !== 'object') {
throw new CommandExecutionError('Sales Navigator lead search API returned malformed lead row');
}
const current = Array.isArray(el.currentPositions) ? el.currentPositions : [];
const past = Array.isArray(el.pastPositions) ? el.pastPositions : [];
const pos = current[0] || past[0] || {};
const name = normalizeWhitespace(el.fullName || [el.firstName, el.lastName].filter(Boolean).join(' '));
if (!name) {
throw new CommandExecutionError('Sales Navigator lead row missing name');
}
const entityUrn = normalizeWhitespace(el.entityUrn || '');
if (!profileUrlFromEntityUrn(entityUrn)) {
throw new CommandExecutionError('Sales Navigator lead row missing profile identity');
}
leads.push({
name,
title: normalizeWhitespace(pos.title || ''),
company: normalizeWhitespace(pos.companyName || ''),
location: normalizeWhitespace(el.geoRegion || ''),
degree: normalizeWhitespace(el.degree || ''),
profile_url: profileUrlFromEntityUrn(entityUrn),
lead_url: leadUrlFromEntityUrn(entityUrn),
recipient_urn: entityUrn,
});
}
return leads;
}
function requireLeadSearchResult(result) {
if (result?.authRequired) {
throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn Sales Navigator API auth failed (HTTP ' + (result.status || '') + '). Confirm the account has Sales Navigator access.');View on GitHub (pinned to 49907e53dc)
Solutions
- Print/log the failing el.entityUrn to confirm the actual URN format LinkedIn now returns.
- Update LEAD_SEARCH_DECORATION (line 11) from a live /sales/search/people request if the decoration version changed.
- Adjust the regex in profileUrlFromEntityUrn (line 66) to match the new URN layout, or update the library to a version that already does.
- Retry with a different keywords query — non-member/placeholder rows may be query-specific.
- As a last resort, patch parseLeads to skip rows without a resolvable entityUrn rather than aborting the whole page.
Example fix
// before
const entityUrn = normalizeWhitespace(el.entityUrn || '');
if (!profileUrlFromEntityUrn(entityUrn)) {
throw new CommandExecutionError('Sales Navigator lead row missing profile identity');
}
// after
const entityUrn = normalizeWhitespace(el.entityUrn || '');
if (!profileUrlFromEntityUrn(entityUrn)) {
console.warn('Skipping lead without parsable entityUrn:', entityUrn);
continue;
} Defensive patterns
Strategy: validation
Validate before calling
function hasParsableEntityUrn(el) {
return typeof el?.entityUrn === 'string'
&& /fs_salesProfile:\(([^,)]+)/.test(el.entityUrn);
}
// pre-check: const usable = json.elements.filter(hasParsableEntityUrn); Type guard
function isSalesProfileUrn(urn) {
return typeof urn === 'string' && /^urn:li:fs_salesProfile:\([^,()]+,.+\)$/.test(urn);
} Try / catch
try {
const pageLeads = parseLeads(json);
} catch (e) {
if (e.message.includes('missing profile identity')) {
// capture a sample URN and compare against the expected fs_salesProfile format
console.error('Unparsable entityUrn; sample:', JSON.stringify(json?.elements?.[0]?.entityUrn));
} else {
throw e;
}
} Prevention
- Log entityUrn values once per run to detect URN format drift early.
- Refresh LEAD_SEARCH_DECORATION whenever LinkedIn redeploys Sales Navigator (the code comment at lines 8-11 flags this).
- Test profileUrlFromEntityUrn against a live response after any library upgrade.
- Keep a fallback that skips rows without parsable URNs if partial results are acceptable.
When it happens
Trigger: A lead row object has an entityUrn that is missing, empty, or does not match /fs_salesProfile:\(([^,)]+)/ (e.g. a different URN format), so profileUrlFromEntityUrn returns '' and the guard at line 93-95 fires.
Common situations: LinkedIn changed the entityUrn format (new URN namespace or extra components) after a Sales Navigator redeploy; rows for company-instead-of-person entities; decorationId bump changing the URN layout; scraping results that include ads or non-member placeholders.
Related errors
- Sales Navigator lead search API returned malformed lead row
- Sales Navigator lead row missing name
- Sales Navigator messaging thread API returned malformed payl
- Sales Navigator messaging thread API returned a thread witho
- Sales Navigator messaging thread API returned malformed mess
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/7b80518f0b99c020.
Report an issue: GitHub.