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

  1. Pass a non-empty positional value: a Sales Navigator inbox URL, thread id, Sales Navigator lead URL, recipient URN, or exact participant name.
  2. Check that the shell variable/CI secret feeding the argument is actually set before invoking the CLI.
  3. Trim the input; a string of only spaces is treated as empty.
  4. 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

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


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