jackwener/OpenCLI · error · ArgumentError
Invalid LinkedIn Learning slug: "${slug}"
Error message
Invalid LinkedIn Learning slug: "${slug}" What it means
Thrown by parseSlug after a slug is extracted (from a URL, a /learning/<slug> path, or the raw input) when it fails the ^[a-zA-Z0-9-_]+$ check. LinkedIn Learning slugs are ASCII letters, digits, hyphens, and underscores only, so characters like spaces, dots, slashes, or non-Latin scripts indicate a malformed identifier.
Source
Thrown at clis/linkedin-learning/course.js:31
let parsed;
try {
parsed = new URL(s);
} catch {
throw new ArgumentError(`Invalid LinkedIn Learning URL: "${s}"`);
}
const host = parsed.hostname.toLowerCase();
if (host !== 'linkedin.com' && host !== 'www.linkedin.com') {
throw new ArgumentError(`Invalid LinkedIn Learning host: "${parsed.hostname}"`);
}
const m = parsed.pathname.match(/^\/learning\/([^/?#]+)/);
if (!m) throw new ArgumentError(`Invalid LinkedIn Learning course URL: "${s}"`);
slug = m[1];
} else {
const m = s.match(/^\/?learning\/([^/?#]+)/);
slug = m ? m[1] : s;
}
if (!/^[a-zA-Z0-9-_]+$/.test(slug)) {
throw new ArgumentError(`Invalid LinkedIn Learning slug: "${slug}"`);
}
return slug;
}
function parseCourse(el, slug) {
const title = normalizeWhitespace(el?.title);
if (!title) return null;
const description = typeof el?.description === 'string'
? el.description
: (el?.description?.text || '');
const duration = el?.duration?.unit === 'SECOND' ? String(el.duration.duration ?? '') : '';
const released = el?.activatedAt ? new Date(el.activatedAt).toISOString().slice(0, 10) : '';
return {
title,
slug,
description,
difficulty: el?.difficultyLevel || '',
duration_sec: duration,View on GitHub (pinned to 49907e53dc)
Solutions
- Pass the ASCII slug exactly as it appears in the /learning/<slug> URL, stripped of query strings, fragments, and trailing slashes.
- Pass the full canonical https://www.linkedin.com/learning/<slug>/ URL instead of hand-editing the slug.
- Look up the course via the linkedin-learning search command to obtain the correct slug.
Example fix
// before opencli linkedin-learning course 'agentic.ai?trk=feed' // after opencli linkedin-learning course 'agentic-ai-build-your-first-agentic-ai-system'
Defensive patterns
Strategy: validation
Validate before calling
const SLUG_RE = /^[a-zA-Z0-9-_]+$/;
if (!SLUG_RE.test(slugOrExtractedSlug)) {
throw new Error(`Bad slug before call: "${slugOrExtractedSlug}"`);
} Type guard
function isValidSlug(v) {
return typeof v === 'string' && /^[a-zA-Z0-9-_]+$/.test(v);
} Try / catch
try {
run(['linkedin-learning', 'course', slug]);
} catch (e) {
if (String(e.message).includes('Invalid LinkedIn Learning slug')) {
// strip query/fragment/unicode, or resolve via search, then retry
} else throw e;
} Prevention
- Strip query strings, fragments, and trailing slashes from anything copied from the address bar.
- Never pass URNs (urn:li:course:...) or localized titles as slugs.
- Obtain slugs from the search command's url column instead of hand-typing them.
When it happens
Trigger: Calling the linkedin-learning course command with a slug containing '.' (e.g. 'agentic.ai'), '/', '?', unicode characters, or trailing punctuation; also non-ASCII localized course titles used as identifiers.
Common situations: Quoting/parsing mistakes that leave URL fragments or query strings attached; copying the localized title rather than the slug from the address bar; course IDs like 'urn:li:course:123' pasted directly.
Related errors
- Invalid LinkedIn Learning host: "${parsed.hostname}"
- Invalid LinkedIn Learning course URL: "${s}"
- --keywords is required
- <train-no> must not be empty
- <train-no> "${trainNo}" does not look like a 12306 internal
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/24a79508925a50c3.
Report an issue: GitHub.