jackwener/OpenCLI · error · ArgumentError
job-url must be a https://www.linkedin.com/jobs/view/<id> UR
Error message
job-url must be a https://www.linkedin.com/jobs/view/<id> URL
What it means
normalizeJobUrl first runs assertSafeLinkedinUrl (rejecting non-LinkedIn/unsafe URLs) and then requires the path to match /jobs/view/<id> or a currentJobId=<id> query param. If neither pattern matches, an ArgumentError is thrown because no job id can be extracted to build the detail query URL.
Source
Thrown at clis/linkedin/job-detail.js:15
import { cli, Strategy } from '@jackwener/opencli/registry';
import { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';
import {
assertLinkedInAuthenticated,
assertSafeLinkedinUrl,
normalizeHttpUrl,
normalizeWhitespace,
unwrapEvaluateResult,
} from './shared.js';
function normalizeJobUrl(value) {
const url = assertSafeLinkedinUrl(value, 'job-url');
const parsed = new URL(url);
const match = parsed.pathname.match(/^\/jobs\/view\/(\d+)/) || parsed.search.match(/[?&]currentJobId=(\d+)/);
if (!match) throw new ArgumentError('job-url must be a https://www.linkedin.com/jobs/view/<id> URL');
return `https://www.linkedin.com/jobs/search/?currentJobId=${match[1]}`;
}
function decodeLinkedinRedirect(url) {
if (!url) return '';
try {
const parsed = new URL(url);
if (parsed.pathname === '/redir/redirect/') return normalizeHttpUrl(parsed.searchParams.get('url') || '');
} catch {}
return normalizeHttpUrl(url);
}
function buildExtractionScript() {
return String.raw`(() => {
const clean = (s) => String(s || '').replace(/[\u00a0\u202f]+/g, ' ').replace(/\s+/g, ' ').trim();
const readRenderedDescription = () => {
const expanders = Array.from(document.querySelectorAll('button, a'))
.filter((el) => /\b(show more|see more|more)\b/i.test(clean(el.innerText || el.textContent || el.getAttribute('aria-label') || '')));View on GitHub (pinned to 49907e53dc)
Solutions
- Pass the full job view URL containing a numeric id: https://www.linkedin.com/jobs/view/<numericId>/.
- If you have a search URL, extract currentJobId from it or open the job and copy the canonical /jobs/view/<id> URL.
- Resolve shortened (lnkd.in) or redirect URLs to their final linkedin.com/jobs/view/<id> form before calling.
- Validate the id client-side: /linkedin\.com\/(jobs\/view\/(\d+)|.*currentJobId=(\d+))/ before invoking.
Example fix
// before
jobDetail('https://www.linkedin.com/jobs/search/?keywords=engineer'); // ArgumentError
// after
jobDetail('https://www.linkedin.com/jobs/view/3987654321/'); Defensive patterns
Strategy: validation
Validate before calling
const JOB_URL_RE = /^https:\/\/www\.linkedin\.com\/jobs\/view\/(\d+)/;
const jobIdFromUrl = (u) => {
const m = String(u).match(JOB_URL_RE) || String(u).match(/[?&]currentJobId=(\d+)/);
if (!m) throw new TypeError('Expected a linkedin.com/jobs/view/<id> URL');
return m[1];
}; Type guard
const isLinkedinJobUrl = (u) => typeof u === 'string' && (/^https:\/\/www\.linkedin\.com\/jobs\/view\/\d+/.test(u) || /[?&]currentJobId=\d+/.test(u));
Try / catch
try {
await linkedinJobDetail(jobUrl);
} catch (e) {
if (e instanceof ArgumentError && /job-url/.test(e.message)) {
const id = await resolveJobIdFromSearchOrRedirect(jobUrl);
return linkedinJobDetail(`https://www.linkedin.com/jobs/view/${id}/`);
}
throw e;
} Prevention
- Store canonical /jobs/view/<numericId> URLs, not search-result or shortener links.
- Resolve lnkd.in and tracking redirects to final URLs before calling.
- Sanitize user-supplied job links with a regex whitelist at your API boundary.
- Extract ids from currentJobId query params when starting from a jobs search page.
When it happens
Trigger: Calling the job-detail command with a job URL like https://www.linkedin.com/jobs/view/some-slug without a numeric id, a jobs collection URL (/jobs/search/...), a truncated URL, or a non-LinkedIn URL that slipped past the safety check but lacks the job-id pattern.
Common situations: Copying a job-search results URL instead of a specific job view URL, URLs where the id lives only in a tracking redirect, sharing mobile app links (linkd.in shorteners) that were never resolved, or malformed saved links from a database.
Related errors
- --limit must be an integer between ${MIN_LIMIT} and ${MAX_LI
- Sales Navigator lead URL must contain resolved profileId, au
- LinkedIn services-read requires a /in/<handle>/ profile URL
- LinkedIn services-read requires a /services/page/<id>/ URL
- ${label} must be a LinkedIn URL
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1b4b9935e5c20d52.
Report an issue: GitHub.