jackwener/OpenCLI · error · CommandExecutionError

LinkedIn connect blocked: ${opened?.reason || 'connect_contr

Error message

LinkedIn connect blocked: ${opened?.reason || 'connect_control_not_found'}

What it means

Button-based Connect: when no invite anchor exists, the command runs an in-page script (buildOpenConnectDialogScript) to click the Connect control directly or via the More menu. If the script reports failure (or returns nothing), the command throws CommandExecutionError naming the script's reason, defaulting to connect_control_not_found.

Source

Thrown at clis/linkedin/connect.js:464

            return [{ status: 'connectable_dry_run', recipient: safety.actualValue, reason: safety.blockReason, profile_url: safety.observedUrl, note_chars: note.length, connectable: true }];
        }
        const inviteHref = probe?.connectHref || '';
        if (inviteHref) {
            // Anchor-based Connect: navigate straight to the invitation route, where the
            // "Add a note?" dialog renders already open.
            const inviteUrl = canonicalizeLinkedInInviteUrl(inviteHref);
            if (!inviteUrl) {
                throw new CommandExecutionError('LinkedIn connect blocked: invalid_connect_link');
            }
            await page.goto(inviteUrl);
            await page.wait(6);
        }
        else {
            // Button-based Connect: no /preload/custom-invite/ anchor exists, so open the
            // invite dialog in-page by clicking the Connect control (directly or via More).
            const opened = unwrapEvaluateResult(await page.evaluate(buildOpenConnectDialogScript(expectedName)));
            if (!opened?.ok) {
                throw new CommandExecutionError(`LinkedIn connect blocked: ${opened?.reason || 'connect_control_not_found'}`);
            }
            await page.wait(3);
        }
        let result = unwrapEvaluateResult(await page.evaluate(buildInviteScript(note)));
        if (result?.reason === 'invite_dialog_not_found') {
            await page.wait(5);
            result = unwrapEvaluateResult(await page.evaluate(buildInviteScript(note)));
        }
        // A button-based Connect that fired the invite without a confirmation dialog leaves
        // no dialog to drive; treat it as sent and let the sent-invitations probe verify.
        if (!result?.ok && result?.reason === 'invite_dialog_not_found' && !inviteHref) {
            result = { ok: true, status: 'sent', reason: 'invitation_sent_no_dialog' };
        }
        if (!result?.ok) throw new CommandExecutionError(`LinkedIn connect blocked: ${result?.reason || 'send_failed'}`);
        // LinkedIn can take a few seconds after the Send click to materialize the
        // new invite in /mynetwork/invitation-manager/sent/. Wait before the
        // first check, then retry page loads for propagation lag.
        await page.wait(8);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Confirm manually that the profile actually shows a Connect button and that you are not already connected or pending
  2. Re-run with a fresh probe — transient render timing can hide the button; increase waits
  3. Update the library if LinkedIn changed the Connect/More button markup
  4. Choose a different target profile if this one genuinely does not offer Connect

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: skip targets already connected or pending
const alreadyConnected = await page.evaluate(() => !!document.querySelector('button[aria-label*="Pending"], .artdeco-button--disabled'));
if (alreadyConnected) throw new Error('Target has no Connect button (already connected/pending or restricted).');

Type guard

null

Try / catch

try { await runConnect(args); } catch (e) { if (String(e.message).includes('connect_control_not_found')) { await page.reload(); await runConnect(args); } else { throw e; } }

Prevention

When it happens

Trigger: The profile has no clickable Connect button: already connected/pending invitation, profile restricts invitations (non-connectable variant that slipped past the routine check), button lives under a More menu the script did not traverse, or LinkedIn renamed/restructured the button.

Common situations: Target already in your network or has a pending invite; premium-only/follow-only profiles; LinkedIn A/B tests moving Connect into a different overflow menu; layout drift breaking the script's selectors.

Related errors


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