jackwener/OpenCLI · error · CommandExecutionError
LinkedIn services-read returned malformed extraction payload
Error message
LinkedIn services-read returned malformed extraction payload
What it means
normalizeServices is the payload validator for services-read extraction. It throws this CommandExecutionError when the evaluate result row is null or not an object, i.e. the in-page extraction script returned something structurally invalid instead of a service record object.
Source
Thrown at clis/linkedin/services-read.js:112
pricing: radios.find((item) => item.checked)?.label || '',
};
})()`;
}
function pairsToMedia(items) {
const lines = Array.isArray(items) ? items.map(normalizeWhitespace).filter(Boolean) : [];
const pairs = [];
for (let i = 0; i < lines.length; i += 2) {
const title = lines[i] || '';
const description = lines[i + 1] || '';
if (title) pairs.push(description ? `${title} — ${description}` : title);
}
return pairs;
}
function normalizeServices(row) {
if (!row || typeof row !== 'object') {
throw new CommandExecutionError('LinkedIn services-read returned malformed extraction payload');
}
const services = Array.isArray(row.services_provided) ? row.services_provided.map(normalizeWhitespace).filter(Boolean) : [];
const mediaItems = pairsToMedia(row.media_lines);
const publicMedia = [];
const serviceUrl = normalizeWhitespace(row.service_url);
const pageTitle = normalizeWhitespace(row.page_title);
const overview = normalizeWhitespace(row.overview);
const availability = normalizeWhitespace(row.availability);
if (!serviceUrl || (!pageTitle && !overview && services.length === 0)) {
throw new CommandExecutionError('LinkedIn services-read could not find stable Services page content');
}
return {
service_url: serviceUrl,
page_title: pageTitle,
overview,
availability,
work_locations: Array.isArray(row.work_locations) ? row.work_locations.map((item) => {
const text = normalizeWhitespace(item);View on GitHub (pinned to 49907e53dc)
Solutions
- Log the raw evaluate payload before normalizeServices to see what the in-page script actually returned.
- Re-run with a fresh browser session; confirm the Services page loads fully before extraction.
- Update the extraction script so it always resolves to a row object (even an empty one) and re-check after LinkedIn markup changes.
Defensive patterns
Strategy: type-guard
Type guard
function isServiceRow(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
} Try / catch
try {
result = await page.evaluate(extractScript);
} catch (e) {
if (!isServiceRow(unwrapEvaluateResult(raw))) throw new Error('extraction returned non-object payload');
} Prevention
- Always unwrap with unwrapEvaluateResult before validating the row.
- Have the in-page script resolve to an object even on failure (e.g. {error: ...}).
- Log the raw payload when this error fires to diagnose page state.
When it happens
Trigger: page.evaluate (via unwrapEvaluateResult) returned null, undefined, an array, or a primitive for the services row — e.g. the injected script hit an unexpected page state and returned no object.
Common situations: LinkedIn served an interstitial/redirect so the script ran on the wrong document; a page.evaluate serialization failure; a scraper version mismatch where the extraction script's contract changed.
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 job detail returned malformed extraction payload
- LinkedIn people search returned malformed extraction payload
- LinkedIn sent invitations returned a malformed extraction pa
- Ctrip bus DOM extraction returned malformed rows
- LinkedIn company extraction returned a malformed current URL
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9b771dd1bdf0935a.
Report an issue: GitHub.