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

  1. Print/log the failing el.entityUrn to confirm the actual URN format LinkedIn now returns.
  2. Update LEAD_SEARCH_DECORATION (line 11) from a live /sales/search/people request if the decoration version changed.
  3. Adjust the regex in profileUrlFromEntityUrn (line 66) to match the new URN layout, or update the library to a version that already does.
  4. Retry with a different keywords query — non-member/placeholder rows may be query-specific.
  5. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/7b80518f0b99c020. Report an issue: GitHub.