{"record":{"id":"6ba2111a315372db","repo":"jackwener/OpenCLI","slug":"recipient-must-resolve-to-a-sales-navigator-lead","errorCode":null,"errorMessage":"--recipient must resolve to a Sales Navigator lead urn","messagePattern":"--recipient must resolve to a Sales Navigator lead urn","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/salesnav-message.js","lineNumber":89,"sourceCode":"  return encodeURIComponent(value).replace(/\\(/g, '%28').replace(/\\)/g, '%29');\n}\n\nfunction profileApiUrl(recipient) {\n  if (!recipient?.profileId || !recipient?.authType || !recipient?.authToken) return '';\n  const key = `(profileId:${recipient.profileId},authType:${recipient.authType},authToken:${recipient.authToken})`;\n  return `https://www.linkedin.com/sales-api/salesApiProfiles/${key}?decoration=${encodeRestliDecoration(PROFILE_DECO)}`;\n}\n\nfunction randomTrackingId() {\n  const bytes = new Uint8Array(8);\n  if (globalThis.crypto?.getRandomValues) globalThis.crypto.getRandomValues(bytes);\n  else for (let i = 0; i < bytes.length; i += 1) bytes[i] = Math.floor(Math.random() * 256);\n  return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nfunction buildCreateMessagePayload({ recipientUrn, subject, body, trackingId = randomTrackingId(), copyToCrm = false }) {\n  const cleanRecipient = normalizeWhitespace(recipientUrn);\n  if (!parseSalesProfileUrn(cleanRecipient)) throw new ArgumentError('--recipient must resolve to a Sales Navigator lead urn');\n  const cleanSubject = normalizeWhitespace(subject);\n  const cleanBody = String(body ?? '').trim();\n  if (!cleanSubject) throw new ArgumentError('--subject is required');\n  if (!cleanBody) throw new ArgumentError('--body is required');\n  if (cleanSubject.length > 200) throw new ArgumentError('--subject must be 200 characters or fewer');\n  if (cleanBody.length > 1900) throw new ArgumentError('--body must be 1900 characters or fewer');\n  return {\n    createMessageRequest: {\n      recipients: [cleanRecipient],\n      subject: cleanSubject,\n      body: cleanBody,\n      copyToCrm: Boolean(copyToCrm),\n      trackingId,\n    },\n  };\n}\n\nfunction extractRemainingCredits(json) {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/salesnav-message.js#L71-L107","documentation":"buildCreateMessagePayload validates that the cleaned recipient is a parseable Sales Navigator lead URN via parseSalesProfileUrn. A recipient that does not match that URN format cannot be used in the createMessageRequest, so an ArgumentError is thrown.","triggerScenarios":"--recipient is set but its value, after whitespace normalization, is not a valid Sales Navigator profile URN (e.g. a person urn of the wrong shape, a profile URL, an email, or a typo).","commonSituations":"Passing a regular LinkedIn profile URL instead of the Sales Navigator lead urn; copying a truncated or HTML-encoded urn from the thread payload; mixing up salesProfile urn with message/thread urns.","solutions":["Obtain the recipient's sales profile urn (urn:li:fs_salesProfile:(urn:li:person:XXX,pageUrn)) from a salesnav search/thread command and pass it verbatim to --recipient.","Do not pass a LinkedIn URL; use the full urn string, properly quoted for your shell.","Decode HTML entities (&#40; &comma;) and remove stray whitespace before passing the urn."],"exampleFix":"// before\n--recipient \"https://www.linkedin.com/sales/lead/ACwAAA123,abc\"\n// after\n--recipient \"urn:li:fs_salesProfile:(urn:li:person:ACwAAA123,urn:li:fsd_profile:abc)\"","handlingStrategy":"validation","validationCode":"const URN_RE = /^urn:li:fs_salesProfile:\\(.+,.*\\)$/;\nif (!URN_RE.test(recipient)) throw new Error(`--recipient must be a sales profile urn, got: ${recipient}`);","typeGuard":"function isSalesProfileUrn(v) { return typeof v === 'string' && v.startsWith('urn:li:fs_salesProfile:(') && v.endsWith(')'); }","tryCatchPattern":"try { await sendMessage({ recipient }); } catch (e) { if (/must resolve to a Sales Navigator lead urn/.test(e.message)) { recipient = await lookupLeadUrn(recipient); /* retry */ } else throw e; }","preventionTips":["Copy the full salesProfile urn verbatim from salesnav search/thread output.","Never pass profile URLs or emails as --recipient.","Decode HTML entities in urns copied from JSON payloads.","Quote the urn in the shell (it contains parentheses and commas)."],"tags":["argument-validation","urn","sales-navigator"],"backgroundTag":"invalid-identifier-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}