jackwener/OpenCLI · error · ArgumentError
thread or recipient is required
Error message
thread or recipient is required
What it means
resolveThreadId is the Sales Navigator thread resolver; it first parses the user-supplied input into a tagged variant. If parseThreadInput classifies the input as 'empty' (missing/whitespace-only thread id, lead URL, recipient URN, or participant name), it throws ArgumentError('thread or recipient is required'). The library throws this to fail fast before doing an expensive inbox scrape it could never match against.
Source
Thrown at clis/linkedin/salesnav-thread.js:138
function threadMatchesInput(row, parsed) {
if (!row || !parsed) return false;
const [kind, criterion] = parsed;
if (kind === 'thread_id') return row.thread_id === criterion;
if (kind === 'recipient_urn') {
return (row.participants || []).some((p) => normalizeWhitespace(p.entity_urn) === criterion);
}
if (kind === 'name') {
const needle = normalizeWhitespace(criterion).toLowerCase();
if (!needle) return false;
if (normalizeWhitespace(row.person_name).toLowerCase() === needle) return true;
return (row.participants || []).some((p) => normalizeWhitespace(p.name).toLowerCase() === needle);
}
return false;
}
async function resolveThreadId(page, input, { maxPages = 30 } = {}) {
const parsed = parseThreadInput(input);
if (parsed[0] === 'empty') throw new ArgumentError('thread or recipient is required');
if (parsed[0] === 'thread_id') return parsed[1];
const inboxRows = await fetchInboxRows(page, { limit: 500, maxPages });
const match = inboxRows.find((row) => threadMatchesInput(row, parsed));
if (!match) {
throw new EmptyResultError('linkedin salesnav-thread', `No Sales Navigator thread matched ${input}`);
}
return match.thread_id;
}
async function fetchThreadWithPagination(page, csrf, threadId, limit = DEFAULT_MESSAGE_LIMIT) {
let requested = THREAD_PAGE_SIZE;
if (limit < requested) requested = limit;
let thread = null;
for (let attempts = 0; attempts < 30; attempts += 1) {
thread = await fetchSalesnavJson(page, csrf, threadApiUrl(threadId, requested), 'Sales Navigator messaging thread API');
const total = Number(thread?.totalMessageCount || 0);
const have = Array.isArray(thread?.messages) ? thread.messages.length : 0;
if (have >= limit || (total && have >= total) || requested >= limit) break;View on GitHub (pinned to 49907e53dc)
Solutions
- Pass a non-empty positional value: a Sales Navigator inbox URL, thread id, Sales Navigator lead URL, recipient URN, or exact participant name.
- Check that the shell variable/CI secret feeding the argument is actually set before invoking the CLI.
- Trim the input; a string of only spaces is treated as empty.
- If you only have a name, use the exact participant name as displayed in Sales Navigator, or resolve to a recipient URN first.
Example fix
// before
node cli.js linkedin salesnav-thread "$THREAD" # THREAD unset -> empty
// after
: "${THREAD:?set THREAD to a Sales Navigator thread id, lead URL, or recipient urn}"
node cli.js linkedin salesnav-thread "$THREAD" Defensive patterns
Strategy: validation
Validate before calling
if (!input || !String(input).trim()) throw new Error('thread-or-recipient must be a non-empty thread id, lead URL, recipient urn, or participant name'); Type guard
function hasThreadInput(v){ return typeof v === 'string' && v.trim().length > 0; } Try / catch
try { await threadId(input) } catch (e) { if (e instanceof ArgumentError && /thread or recipient is required/.test(e.message)) { /* surface config problem */ } else throw e; } Prevention
- Check the feeding shell/config variable for emptiness before invoking (use ${VAR:?} in bash).
- Trim inputs before passing; whitespace-only is treated as empty.
- Prefer passing an explicit thread id or URN over free-text names.
- Add a CI assertion that required CLI arguments are non-empty.
When it happens
Trigger: Calling the 'linkedin salesnav-thread' CLI (via threadId) with an undefined, null, or whitespace-only positional 'thread-or-recipient' argument so parseThreadInput returns ['empty'].
Common situations: Scripting the CLI from a shell variable that is unset or empty (e.g. $THREAD was never exported); passing an argument that normalizes to whitespace; wiring where the positional argument was given to the wrong flag name.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- thread-or-recipient is required
- Unknown 12306 station telecode "${trimmed}"
- Unknown 12306 station "${trimmed}"
- date must be YYYY-MM-DD, got "${value}"
- date "${value}" is not a real calendar date
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/94236ffbd61cecf9.
Report an issue: GitHub.