{"record":{"id":"b916b4685ea4dbaf","repo":"jackwener/OpenCLI","slug":"linkedin-services-read-requires-a-services-page","errorCode":null,"errorMessage":"LinkedIn services-read requires a /services/page/<id>/ URL","messagePattern":"LinkedIn services-read requires a /services/page/<id>/ URL","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/linkedin/services-read.js","lineNumber":23,"sourceCode":"  assertSafeLinkedinUrl,\n  normalizeWhitespace,\n  unwrapEvaluateResult,\n} from './shared.js';\n\nfunction normalizeProfileUrl(value) {\n  const url = assertSafeLinkedinUrl(value || 'https://www.linkedin.com/in/me/', 'profile-url', '/in/me/');\n  const parsed = new URL(url);\n  if (!/^\\/in\\/[^/?#]+\\/?$/.test(parsed.pathname)) {\n    throw new CommandExecutionError('LinkedIn services-read requires a /in/<handle>/ profile URL');\n  }\n  return parsed.toString();\n}\n\nfunction normalizeServicesUrl(value) {\n  const url = assertSafeLinkedinUrl(value, 'services-url', '/services/page/');\n  const parsed = new URL(url);\n  if (!/^\\/services\\/page\\/[^/?#]+\\/?$/.test(parsed.pathname)) {\n    throw new CommandExecutionError('LinkedIn services-read requires a /services/page/<id>/ URL');\n  }\n  return parsed.toString();\n}\n\nfunction buildFindServicesUrlScript() {\n  return String.raw`(() => {\n    const link = Array.from(document.querySelectorAll('a[href*=\"/services/page/\"]'))\n      .map((a) => a.href || '')\n      .find(Boolean);\n    return { services_url: link || '' };\n  })()`;\n}\n\nfunction buildServicesPageScript() {\n  return String.raw`(() => {\n    const clean = (s) => String(s || '').replace(/[\\u00a0\\u202f]+/g, ' ').replace(/\\s+/g, ' ').trim();\n    const lines = (document.body?.innerText || '').split(/\\n+/).map(clean).filter(Boolean);\n    const unique = (items) => Array.from(new Set(items.filter(Boolean)));","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/linkedin/services-read.js#L5-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait runCommand('linkedin services-read', ['--services-url', 'https://www.linkedin.com/in/jane-doe/']);\n// after\nawait runCommand('linkedin services-read', ['--services-url', 'https://www.linkedin.com/services/page/1234abc/']);","handlingStrategy":"validation","validationCode":"function isServicesUrl(u) {\n  try { return /^\\/services\\/page\\/[^/?#]+\\/?$/.test(new URL(u, 'https://www.linkedin.com').pathname); }\n  catch { return false; }\n}\nif (servicesUrl && !isServicesUrl(servicesUrl)) throw new Error('services-url must be /services/page/<id>/');","typeGuard":"function isLinkedInServicesUrl(v) {\n  try {\n    const p = new URL(String(v), 'https://www.linkedin.com');\n    return /^\\/services\\/page\\/[^/?#]+\\/?$/.test(p.pathname);\n  } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["linkedin","argument-validation","url"],"backgroundTag":"invalid-url-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}