jackwener/OpenCLI · error · CommandExecutionError
Sales Navigator InMail blocked by recipient restriction
Error message
Sales Navigator InMail blocked by recipient restriction
What it means
The library fetched the recipient's Sales Navigator profile and found that the profile's inmail_restriction field is not NO_RESTRICTION, meaning LinkedIn blocks sending an InMail to this recipient. It throws before consuming any InMail credit, because the send would fail or be silently disallowed.
Source
Thrown at clis/linkedin/salesnav-message.js:285
if (!page) throw new CommandExecutionError('Browser session required for linkedin salesnav-message');
const recipientArg = requireStringArg(args, 'recipient', '--recipient');
const subject = requireStringArg(args, 'subject', '--subject');
const body = String(args.body ?? '').trim();
if (!body) throw new ArgumentError('--body is required');
await page.goto(SALES_HOME);
await page.wait(4);
const csrf = await getCsrf(page);
const recipient = await resolveRecipient(page, parseRecipient(recipientArg), csrf);
let summary = { recipient: '', title: '', company: '', degree: '', inmail_restriction: '', open_link: false };
const profileUrl = profileApiUrl(recipient);
if (profileUrl) {
const profileResult = requireFetchResult(unwrapEvaluateResult(await page.evaluate(fetchJsonScript(profileUrl, csrf))), 'LinkedIn Sales Navigator profile API');
summary = requireProfileSummary(profileResult.json);
}
if (summary.inmail_restriction && summary.inmail_restriction !== 'NO_RESTRICTION') {
throw new CommandExecutionError('Sales Navigator InMail blocked by recipient restriction', summary.inmail_restriction);
}
const creditsResult = requireFetchResult(unwrapEvaluateResult(await page.evaluate(fetchJsonScript(CREDITS_URL, csrf))), 'LinkedIn Sales Navigator credits API');
const creditsRemaining = extractRemainingCredits(creditsResult?.json);
const payload = buildCreateMessagePayload({ recipientUrn: recipient.entityUrn, subject, body, copyToCrm: args['copy-to-crm'] });
if (!args.send) {
return [{
status: 'validated_dry_run',
recipient: summary.recipient,
title: summary.title,
company: summary.company,
credits_remaining: creditsRemaining,
credits_before: creditsRemaining,
credits_after: '',
sent_in_salesnav: false,
message_chars: body.length,
subject_chars: subject.length,View on GitHub (pinned to 49907e53dc)
Solutions
- Pick a different recipient or contact path (regular LinkedIn message/connection request) — the restriction is on LinkedIn's side and cannot be bypassed.
- Re-fetch the profile to confirm the restriction value is current, then skip or queue the recipient in your outreach tooling.
- Check the thrown detail (the restriction value) and map it in your caller to decide whether retrying later is meaningful (usually not — restrictions are persistent).
Example fix
// before
await sendSalesNavInMail({ recipient, subject, body });
// after
const profile = await fetchProfileSummary(recipient);
if (profile.inmail_restriction && profile.inmail_restriction !== 'NO_RESTRICTION') {
console.warn(`Skipping ${recipient.id}: ${profile.inmail_restriction}`);
return { status: 'skipped', reason: profile.inmail_restriction };
}
await sendSalesNavInMail({ recipient, subject, body }); Defensive patterns
Strategy: try-catch
Validate before calling
const summary = await fetchProfileSummary(recipient);
if (summary.inmail_restriction && summary.inmail_restriction !== 'NO_RESTRICTION') {
throw new SkipError(`Recipient restricted: ${summary.inmail_restriction}`);
} Type guard
function canSendInMail(summary) {
return !!summary && (summary.inmail_restriction ?? 'NO_RESTRICTION') === 'NO_RESTRICTION';
} Try / catch
try {
await sendSalesNavInMail(recipient, { subject, body });
} catch (e) {
if (e.detail && e.detail !== 'NO_RESTRICTION') {
markRecipientBlocked(recipient, e.detail);
return { status: 'skipped' };
}
throw e;
} Prevention
- Pre-screen recipients' inmail_restriction in bulk before campaigns
- Keep a blocklist of restricted recipients
- Do not auto-retry InMail sends — restrictions are persistent
- Map restriction codes to alternate contact channels
When it happens
Trigger: Calling the salesnav-message CLI for a recipient whose LinkedIn profile has an InMail restriction (e.g. recipient opted out of InMails, is outside your network with restricted messaging, or has a 'NO_INMAIL' style restriction value other than NO_RESTRICTION).
Common situations: Messaging open-to-work-only or InMail-opted-out candidates; contacting recipients whose accounts limit commercial messages; sending to profiles the operator's seat cannot reach (tier/seat restrictions); stale profile summary cached before recipient changed settings.
Related errors
- Sales Navigator messaging threads API returned malformed pay
- Sales Navigator messaging threads API returned malformed thr
- Sales Navigator messaging thread row missing id
- ${label} authentication failed (HTTP ${result.status || 'aut
- ${label} returned an unexpected response
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/0aac8b528f58920a.
Report an issue: GitHub.