{"record":{"id":"78626221ab8599df","repo":"jackwener/OpenCLI","slug":"sales-navigator-lead-url-must-contain-resolved-pro","errorCode":null,"errorMessage":"Sales Navigator lead URL must contain resolved profileId, authType, and authToken","messagePattern":"Sales Navigator lead URL must contain resolved profileId, authType, and authToken","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-thread.js","lineNumber":51,"sourceCode":"function parseThreadInput(value) {\n  const raw = normalizeWhitespace(value);\n  if (!raw) return ['empty', ''];\n  if (/^2-[A-Za-z0-9+/=_-]+$/.test(raw)) return ['thread_id', raw];\n  if (/^urn:li:fs_salesProfile:\\(/.test(raw)) {\n    const urn = parseSalesProfileUrn(raw);\n    if (!urn) throw new ArgumentError('Sales Navigator recipient urn must be urn:li:fs_salesProfile:(profileId,authType,authToken)');\n    return ['recipient_urn', urn];\n  }\n  try {\n    const url = new URL(raw);\n    if (url.protocol !== 'https:' || url.username || url.password || url.port || !isLinkedInHost(url.hostname)) return ['name', raw.toLowerCase()];\n    const inboxMatch = url.pathname.match(/^\\/sales\\/inbox\\/([^/]+)\\/?$/i);\n    if (inboxMatch) return ['thread_id', decodeURIComponent(inboxMatch[1])];\n    const leadMatch = url.pathname.match(/^\\/sales\\/lead\\/([^,/]+),([^,/]+),([^/]+)\\/?$/i);\n    if (leadMatch) {\n      const urn = `urn:li:fs_salesProfile:(${decodeURIComponent(leadMatch[1])},${decodeURIComponent(leadMatch[2])},${decodeURIComponent(leadMatch[3])})`;\n      if (!parseSalesProfileUrn(urn)) {\n        throw new ArgumentError('Sales Navigator lead URL must contain resolved profileId, authType, and authToken');\n      }\n      return ['recipient_urn', urn];\n    }\n  } catch (err) {\n    if (err instanceof ArgumentError) throw err;\n    // Fall through to name matching for non-URL text.\n  }\n  return ['name', raw.toLowerCase()];\n}\n\nfunction salesnavThreadUrl(threadId) {\n  return threadId ? `https://www.linkedin.com/sales/inbox/${encodeURIComponent(threadId)}` : '';\n}\n\nfunction threadApiUrl(threadId, messageCount) {\n  return `${THREADS_BASE}/${encodeURIComponent(threadId)}?decoration=${encodeRestliDecoration(THREAD_DECORATION)}&count=1&messageCount=${messageCount}`;\n}\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-thread.js#L33-L69","documentation":"An ArgumentError from parseThreadInput (clis/linkedin/salesnav-thread.js:51) raised when a https://www.linkedin.com/sales/lead/... URL matches the lead path but its three path segments do not form a valid salesProfile URN. parseSalesProfileUrn rejects segments that are empty, 'undefined', 'null', or 'not_available', which typically means the lead URL was captured from a page where profile identity fields were unresolved. The library throws rather than silently treating the URL as a name.","triggerScenarios":"Passing a lead URL like https://www.linkedin.com/sales/lead/ACoAABC,not_available,XYZ/ scraped from an unresolved profile card; a URL whose segments were percent-encoded objects ('undefined') from string interpolation; malformed URLs with missing third segment matching only via regex leniency.","commonSituations":"Scraping lead URLs from cached/preview pages where authToken was not yet resolved; templating URLs from data objects with undefined fields; copying lead URLs from older Sales Navigator exports with a different segment layout.","solutions":["Open the lead URL in a browser; if it 404s or shows an unresolved profile, get a fresh URL from a live salesnav-search run (lead_url column).","Decode the three segments and verify none are empty, 'undefined', 'null', or 'not_available' before passing the URL.","If only the profileId is known, resolve the full lead URL via salesnav-search or fall back to passing the person's exact name.","Re-copy the URL from the live Sales Navigator lead page rather than from logs or cached search results."],"exampleFix":"// before\nawait run('linkedin salesnav-thread', ['https://www.linkedin.com/sales/lead/ACoAABC,undefined,AbCd/']);\n// after (validate segments first)\nconst m = url.pathname.match(/^\\/sales\\/lead\\/([^,/]+),([^,/]+),([^/]+)\\/?$/);\nconst segs = m.slice(1).map(decodeURIComponent);\nif (segs.some((s) => !s || s === 'undefined' || s === 'not_available')) {\n  throw new Error('unresolved lead URL segments');\n}","handlingStrategy":"validation","validationCode":"// Validate lead URL segments before passing them:\nfunction leadUrlSegmentsAreResolved(url) {\n  const m = url.match(/^https:\\/\\/www\\.linkedin\\.com\\/sales\\/lead\\/([^,/]+),([^,/]+),([^/]+)\\/?$/);\n  if (!m) return false;\n  return m.slice(1).every((s) => {\n    const v = decodeURIComponent(s);\n    return v && !['undefined', 'null', 'not_available'].includes(v);\n  });\n}\nif (!leadUrlSegmentsAreResolved(leadUrl)) throw new Error('lead URL has unresolved profileId/authType/authToken');","typeGuard":"function isResolvedLeadUrl(value) {\n  if (typeof value !== 'string') return false;\n  const m = value.match(/linkedin\\.com\\/sales\\/lead\\/([^,/]+),([^,/]+),([^/]+)\\/?$/);\n  if (!m) return false;\n  return m.slice(1).map(decodeURIComponent).every(\n    (s) => s && !['undefined', 'null', 'not_available'].includes(s)\n  );\n}","tryCatchPattern":"try {\n  await run('linkedin salesnav-thread', [leadUrl]);\n} catch (err) {\n  if (err instanceof ArgumentError && /lead URL/.test(err.message)) {\n    console.error('Lead URL unresolved; re-fetch via salesnav-search lead_url column.');\n    return run('linkedin salesnav-thread', [lead.name]);\n  }\n  throw err;\n}","preventionTips":["Take lead_url values from live salesnav-search output, not cached pages or scraped previews.","Decode and check the three segments for 'undefined'/'not_available' whenever URLs pass through templates or logs.","Fall back to the participant's exact name when profile identity fields are unresolved."],"tags":["linkedin","argument-validation","url","sales-navigator"],"backgroundTag":"invalid-urn-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}