jackwener/OpenCLI · error · ArgumentError
--recipient must resolve to a Sales Navigator lead urn
Error message
--recipient must resolve to a Sales Navigator lead urn
What it means
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.
Source
Thrown at clis/linkedin/salesnav-message.js:89
return encodeURIComponent(value).replace(/\(/g, '%28').replace(/\)/g, '%29');
}
function profileApiUrl(recipient) {
if (!recipient?.profileId || !recipient?.authType || !recipient?.authToken) return '';
const key = `(profileId:${recipient.profileId},authType:${recipient.authType},authToken:${recipient.authToken})`;
return `https://www.linkedin.com/sales-api/salesApiProfiles/${key}?decoration=${encodeRestliDecoration(PROFILE_DECO)}`;
}
function randomTrackingId() {
const bytes = new Uint8Array(8);
if (globalThis.crypto?.getRandomValues) globalThis.crypto.getRandomValues(bytes);
else for (let i = 0; i < bytes.length; i += 1) bytes[i] = Math.floor(Math.random() * 256);
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
}
function buildCreateMessagePayload({ recipientUrn, subject, body, trackingId = randomTrackingId(), copyToCrm = false }) {
const cleanRecipient = normalizeWhitespace(recipientUrn);
if (!parseSalesProfileUrn(cleanRecipient)) throw new ArgumentError('--recipient must resolve to a Sales Navigator lead urn');
const cleanSubject = normalizeWhitespace(subject);
const cleanBody = String(body ?? '').trim();
if (!cleanSubject) throw new ArgumentError('--subject is required');
if (!cleanBody) throw new ArgumentError('--body is required');
if (cleanSubject.length > 200) throw new ArgumentError('--subject must be 200 characters or fewer');
if (cleanBody.length > 1900) throw new ArgumentError('--body must be 1900 characters or fewer');
return {
createMessageRequest: {
recipients: [cleanRecipient],
subject: cleanSubject,
body: cleanBody,
copyToCrm: Boolean(copyToCrm),
trackingId,
},
};
}
function extractRemainingCredits(json) {View on GitHub (pinned to 49907e53dc)
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 (( ,) and remove stray whitespace before passing the urn.
Example fix
// before --recipient "https://www.linkedin.com/sales/lead/ACwAAA123,abc" // after --recipient "urn:li:fs_salesProfile:(urn:li:person:ACwAAA123,urn:li:fsd_profile:abc)"
Defensive patterns
Strategy: validation
Validate before calling
const URN_RE = /^urn:li:fs_salesProfile:\(.+,.*\)$/;
if (!URN_RE.test(recipient)) throw new Error(`--recipient must be a sales profile urn, got: ${recipient}`); Type guard
function isSalesProfileUrn(v) { return typeof v === 'string' && v.startsWith('urn:li:fs_salesProfile:(') && v.endsWith(')'); } Try / catch
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; } Prevention
- 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).
When it happens
Trigger: --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).
Common situations: 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.
Related errors
- Sales Navigator recipient urn must be urn:li:fs_salesProfile
- --recipient must be a Sales Navigator lead URL, Sales Naviga
- Sales Navigator lead URL must contain resolved profileId, au
- <train-no> must not be empty
- <train-no> "${trainNo}" does not look like a 12306 internal
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/6ba2111a315372db.
Report an issue: GitHub.