jackwener/OpenCLI · error · CommandExecutionError
LinkedIn services-read requires a /services/page/<id>/ URL
Error message
LinkedIn services-read requires a /services/page/<id>/ URL
What it means
normalizeServicesUrl validates the --services-url argument for linkedin services-read. After the safety check, the pathname must match /services/page/<id>/. Other LinkedIn URLs are rejected with this ArgumentError because the command only extracts data from Services pages.
Source
Thrown at clis/linkedin/services-read.js:23
assertSafeLinkedinUrl,
normalizeWhitespace,
unwrapEvaluateResult,
} from './shared.js';
function normalizeProfileUrl(value) {
const url = assertSafeLinkedinUrl(value || 'https://www.linkedin.com/in/me/', 'profile-url', '/in/me/');
const parsed = new URL(url);
if (!/^\/in\/[^/?#]+\/?$/.test(parsed.pathname)) {
throw new CommandExecutionError('LinkedIn services-read requires a /in/<handle>/ profile URL');
}
return parsed.toString();
}
function normalizeServicesUrl(value) {
const url = assertSafeLinkedinUrl(value, 'services-url', '/services/page/');
const parsed = new URL(url);
if (!/^\/services\/page\/[^/?#]+\/?$/.test(parsed.pathname)) {
throw new CommandExecutionError('LinkedIn services-read requires a /services/page/<id>/ URL');
}
return parsed.toString();
}
function buildFindServicesUrlScript() {
return String.raw`(() => {
const link = Array.from(document.querySelectorAll('a[href*="/services/page/"]'))
.map((a) => a.href || '')
.find(Boolean);
return { services_url: link || '' };
})()`;
}
function buildServicesPageScript() {
return String.raw`(() => {
const clean = (s) => String(s || '').replace(/[\u00a0\u202f]+/g, ' ').replace(/\s+/g, ' ').trim();
const lines = (document.body?.innerText || '').split(/\n+/).map(clean).filter(Boolean);
const unique = (items) => Array.from(new Set(items.filter(Boolean)));View on GitHub (pinned to 49907e53dc)
Solutions
- Pass the full Services page URL like https://www.linkedin.com/services/page/<id>/ — copy it from the profile's Services link.
- Omit --services-url and let the command discover it from --profile-url or the default /in/me/ profile.
- If the id is missing from your saved URL, re-discover it via the profile rather than hand-editing the path.
Example fix
// before
await runCommand('linkedin services-read', ['--services-url', 'https://www.linkedin.com/in/jane-doe/']);
// after
await runCommand('linkedin services-read', ['--services-url', 'https://www.linkedin.com/services/page/1234abc/']); Defensive patterns
Strategy: validation
Validate before calling
function isServicesUrl(u) {
try { return /^\/services\/page\/[^/?#]+\/?$/.test(new URL(u, 'https://www.linkedin.com').pathname); }
catch { return false; }
}
if (servicesUrl && !isServicesUrl(servicesUrl)) throw new Error('services-url must be /services/page/<id>/'); Type guard
function isLinkedInServicesUrl(v) {
try {
const p = new URL(String(v), 'https://www.linkedin.com');
return /^\/services\/page\/[^/?#]+\/?$/.test(p.pathname);
} catch { return false; }
} Prevention
- Store the full Services page URL with its id, never a truncated /services/ link.
- Prefer discovery via --profile-url over hand-assembled services URLs.
- Sanitize pasted URLs by re-parsing and rebuilding them before use.
When it happens
Trigger: Passing --services-url that is not a /services/page/<id>/ path, e.g. a profile URL, a /services/ root link, or a truncated /services/page/ with no id.
Common situations: Pasting the profile URL into --services-url by mistake; LinkedIn redesigns the Services page and links move off /services/page/; copying a relative /services/ link without the page id.
Related errors
- job-url must be a https://www.linkedin.com/jobs/view/<id> UR
- Sales Navigator lead URL must contain resolved profileId, au
- LinkedIn services-read requires a /in/<handle>/ profile URL
- ${label} must be a LinkedIn URL
- ${label} must be an https LinkedIn URL without credentials o
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/b916b4685ea4dbaf.
Report an issue: GitHub.