jackwener/OpenCLI · error · CommandExecutionError
Sales Navigator lead row missing name
Error message
Sales Navigator lead row missing name
What it means
parseLeads requires every lead row to have a resolvable display name (el.fullName, or firstName+lastName joined) and throws CommandExecutionError when normalizeWhitespace yields an empty string. The library treats a nameless lead row as unusable data since the name is the primary human-identifiable output column.
Source
Thrown at clis/linkedin/salesnav-search.js:90
if (!match) return '';
return `https://www.linkedin.com/sales/lead/${encodeURIComponent(match[1])},${encodeURIComponent(match[2])},${encodeURIComponent(match[3])}`;
}
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;
}View on GitHub (pinned to 49907e53dc)
Solutions
- Retry the search; ghost/anonymous rows are often transient and tied to session state.
- Sign in to LinkedIn with Sales Navigator access in the automation browser so restricted profile data is served.
- Inspect the offending row in devtools (salesApiLeadSearch response) to see which name field changed or is absent.
- Patch parseLeads to skip nameless rows instead of throwing if partial results are acceptable to your workflow.
- Update the CLI/library in case upstream adapted to a renamed/added name field in the decoration schema.
Example fix
// before
if (!name) {
throw new CommandExecutionError('Sales Navigator lead row missing name');
}
// after
if (!name) {
console.warn('Skipping lead row without name:', el.entityUrn);
continue; // skip instead of aborting the whole search
} Defensive patterns
Strategy: validation
Validate before calling
function leadHasName(el) {
const n = String(el?.fullName || [el?.firstName, el?.lastName].filter(Boolean).join(' ') || '').trim();
return n.length > 0;
}
// pre-check a page: const usable = json.elements.filter(leadHasName); Type guard
function hasName(el) {
return typeof el === 'object' && el !== null
&& (typeof el.fullName === 'string' && el.fullName.trim() !== ''
|| (typeof el.firstName === 'string' && el.firstName.trim() !== ''));
} Try / catch
try {
const pageLeads = parseLeads(json);
} catch (e) {
if (e.message.includes('missing name')) {
console.warn('Nameless lead row skipped; possibly a restricted/anonymized profile');
} else {
throw e;
}
} Prevention
- Use a Sales Navigator-enabled account so restricted profiles still return names.
- Prefer narrower keywords queries that surface full member profiles rather than anonymized rows.
- Filter rows client-side (fullName/firstName/lastName present) before passing them to strict parsers.
- Expect anonymous/ghost rows near result boundaries and treat single-row failures as skippable.
When it happens
Trigger: A json.elements entry is an object but has no fullName, firstName, or lastName (or they are empty/whitespace-only), so the constructed name is '' and the guard at line 89-91 fires.
Common situations: Search results including anonymized/redacted profiles (e.g. out-of-network private mode), LinkedIn returning ghost rows for deactivated accounts, restricted profiles where names are hidden, or schema changes renaming the fullName field after a Sales Navigator redeploy.
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
- Sales Navigator messaging thread API returned a thread witho
- Sales Navigator lead search API returned malformed lead row
- Sales Navigator lead row missing profile identity
- Sales Navigator messaging thread API returned malformed payl
- Sales Navigator messaging thread API returned malformed mess
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/17b00e2039ccc46e.
Report an issue: GitHub.