jackwener/OpenCLI · error · CommandExecutionError
LinkedIn sent invitations contained a malformed invitation c
Error message
LinkedIn sent invitations contained a malformed invitation card.
What it means
Card candidates that have both a profile link and a Withdraw action but yield no parseable name or profile URL are counted in malformedCount. The command treats any malformed card as a hard failure (no silent partial data for CRM reconciliation) and throws this CommandExecutionError listing nothing but signaling the DOM produced an unparseable invitation card.
Source
Thrown at clis/linkedin/sent-invitations.js:105
browser: true,
args: [],
columns: ['rank', 'name', 'profile_url', 'invited_date_text'],
func: async (page) => {
if (!page) throw new CommandExecutionError('Browser session required for linkedin sent-invitations');
await page.goto(SENT_URL);
await page.wait(12);
let result = unwrapEvaluateResult(await page.evaluate(buildSentInvitationsScript()));
if (result?.authRequired) {
throw new AuthRequiredError(LINKEDIN_DOMAIN, 'LinkedIn sent invitations requires an active signed-in browser session.');
}
if (result?.warning) {
throw new CommandExecutionError('LinkedIn warning/restriction state visible on sent invitations page.');
}
if (!result || typeof result !== 'object' || Array.isArray(result) || !Array.isArray(result.rows)) {
throw new CommandExecutionError('LinkedIn sent invitations returned a malformed extraction payload.');
}
if (result.malformedCount > 0) {
throw new CommandExecutionError('LinkedIn sent invitations contained a malformed invitation card.');
}
const rows = result.rows;
if (rows.length === 0) {
if (result.explicitEmpty) {
throw new EmptyResultError('linkedin sent-invitations', 'No pending sent invitations were found.');
}
throw new CommandExecutionError('LinkedIn sent invitation cards were not found; the page structure may have changed.');
}
return rows.map((row, index) => ({
rank: index + 1,
name: row.name || '',
profile_url: row.profile_url || '',
invited_date_text: row.invited_date_text || '',
}));
},
});
export const __test__ = {View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the invitation-manager page in the browser to see which card fails to parse; if a member was deleted/deactivated, that card may be inherently empty — consider handling it upstream in the extraction script.
- Update the extraction selectors/regexes in buildSentInvitationsScript to match the current LinkedIn card markup and aria-label patterns.
- Disable browser extensions in the automation profile and retry with the UI language set to English so the /^withdraw/i and date regexes match.
- Clear the checkpoint by reloading the page; if only transient rendering caused it, a re-run after a full load (increase the 12s wait) may parse all cards.
Example fix
// before (strict fail on any malformed card)
if (result.malformedCount > 0) throw new CommandExecutionError('...malformed invitation card.');
// after (tolerate known-empty cards for deleted members)
if (result.malformedCount > 0) {
console.error(`[linkedin] skipped ${result.malformedCount} unparseable card(s)`);
// or: filter candidate cards lacking a[href*="/in/"] before counting
} Defensive patterns
Strategy: fallback
Validate before calling
null
Type guard
const isWellFormedCard = (card) => {
const link = card?.querySelector?.('a[href*="/in/"]');
const name = link?.innerText || link?.textContent || link?.getAttribute('aria-label') || '';
return Boolean(name.trim()) && Boolean(link?.getAttribute('href'));
}; Try / catch
try {
const rows = await run(['linkedin', 'sent-invitations']);
} catch (e) {
if (/malformed invitation card/.test(e.message)) {
console.warn('LinkedIn card markup not fully parseable; falling back to manual export of the invitation manager page.');
// Fall back to a manual/CSV flow instead of failing the CRM sync
return manualInvitationExport();
}
throw e;
} Prevention
- Pin the automation browser's UI language to English so /^withdraw/i and 'Sent X ago' regexes match.
- Disable DOM-altering extensions in the automation profile.
- Increase the post-navigation wait if cards render lazily, so parsing sees fully hydrated cards.
- After LinkedIn UI updates, re-verify selectors on the invitation-manager page and update the extraction script promptly.
When it happens
Trigger: Running `opencli linkedin sent-invitations` when at least one invitation card on the page fails parsing: no name derivable from the withdraw aria-label, profile link text, or card lines; or the /in/ link's href is empty — i.e. LinkedIn renders a card variant the extractors do not recognize.
Common situations: LinkedIn UI update changing invitation card markup (new layout, renamed aria-labels); a card for a deleted/deactivated member rendering with missing name/href; localized UI text breaking the /^withdraw .../i and 'Sent X days ago' regexes; injected browser extensions altering the DOM.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- LinkedIn people search returned malformed extraction payload
- LinkedIn people search found profile candidates but could no
- Not a git repository
- Working tree not clean: ${status}
- Ctrip attraction DOM extraction returned malformed rows
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/a343c7e5529372e9.
Report an issue: GitHub.