jackwener/OpenCLI · error · CommandExecutionError

LinkedIn services-read requires a /in/<handle>/ profile URL

Error message

LinkedIn services-read requires a /in/<handle>/ profile URL

What it means

normalizeProfileUrl validates the --profile-url argument for linkedin services-read. After assertSafeLinkedinUrl confirms it is a safe linkedin.com URL, the pathname must match /in/<handle>/. Anything else (company pages, feed URLs, posts) is rejected with this ArgumentError because Services discovery only works on personal profile pages.

Source

Thrown at clis/linkedin/services-read.js:14

import { cli, Strategy } from '@jackwener/opencli/registry';
import { CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';
import {
  assertLinkedInAuthenticated,
  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);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a personal profile URL of the form https://www.linkedin.com/in/<handle>/ to --profile-url.
  2. If you only have a company page, use --services-url with the /services/page/<id>/ URL directly instead.
  3. Omit --profile-url entirely so the command defaults to /in/me/ (the logged-in profile).

Example fix

// before
await runCommand('linkedin services-read', ['--profile-url', 'https://www.linkedin.com/company/acme/']);
// after
await runCommand('linkedin services-read', ['--profile-url', 'https://www.linkedin.com/in/jane-doe/']);
Defensive patterns

Strategy: validation

Validate before calling

function isProfileUrl(u) {
  try { return /^\/in\/[^/?#]+\/?$/.test(new URL(u, 'https://www.linkedin.com').pathname); }
  catch { return false; }
}
if (!isProfileUrl(profileUrl)) throw new Error('profile-url must be https://www.linkedin.com/in/<handle>/');

Type guard

function isLinkedInProfileUrl(v) {
  try {
    const p = new URL(String(v), 'https://www.linkedin.com');
    return p.hostname === 'www.linkedin.com' && /^\/in\/[^/?#]+\/?$/.test(p.pathname);
  } catch { return false; }
}

Prevention

When it happens

Trigger: Passing --profile-url that is not a /in/<handle>/ path, e.g. https://www.linkedin.com/company/foo/, https://www.linkedin.com/feed/, a bare handle like 'john-doe', or a /in/ URL with an empty handle.

Common situations: Copy-pasting a company page or a public post URL instead of the personal profile; passing a vanity name without the /in/ prefix; trailing query strings are fine but a /in/ path without a handle is not.

Related errors


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