jackwener/OpenCLI · error · CommandExecutionError

${label} failed

Error message

${label} failed

What it means

When the fetch response body contains an error field, requireFetchResult wraps it in CommandExecutionError with message '${label} failed' and the upstream error as detail. This is LinkedIn's Sales Navigator API reporting a request-level failure (not auth, not malformed output).

Source

Thrown at clis/linkedin/salesnav-message.js:166

    } catch (e) {
      return ['error', 0, null, '', 'fetch failed: ' + ((e && e.message) || String(e))];
    }
  })()`;
}

function requireFetchResult(result, label, { requireJson = true } = {}) {
  if (Array.isArray(result)) {
    const [kind, status, json, text, error] = result;
    result = {
      authRequired: kind === 'auth',
      error: kind === 'error' ? error || `HTTP ${status}` : '',
      status,
      json,
      text,
    };
  }
  if (result?.authRequired) throw new AuthRequiredError(LINKEDIN_DOMAIN, `${label} auth failed.`);
  if (result?.error) throw new CommandExecutionError(`${label} failed`, result.error);
  if (!result || typeof result !== 'object' || Array.isArray(result)) {
    throw new CommandExecutionError(`${label} returned malformed response`);
  }
  if (requireJson && (!result.json || typeof result.json !== 'object' || Array.isArray(result.json))) {
    throw new CommandExecutionError(`${label} returned malformed response`, 'missing_json');
  }
  return result;
}

function salesPageShowsSentMessage(text, recipientName) {
  const normalizedText = normalizeWhitespace(text);
  const firstName = normalizeWhitespace(recipientName).split(' ')[0];
  return normalizedText.includes('You sent a Sales Navigator message')
    && (!firstName || normalizedText.includes(firstName));
}

async function getCsrf(page) {
  const cookies = await page.getCookies({ url: 'https://www.linkedin.com' });

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the error detail (second argument) — it carries LinkedIn's upstream message
  2. Retry with a different, fully-resolved recipient urn (authType/authToken present)
  3. Re-check payload constraints (subject <=200 chars, body <=1900) before calling
  4. Re-verify the Sales Navigator API response shape after LinkedIn UI updates

Example fix

// before
await sendInMail(page, { subject: longSubject, body });
// after
if (subject.length > 200) subject = subject.slice(0, 200);
await sendInMail(page, { subject, body });
Defensive patterns

Strategy: try-catch

Validate before calling

// validate payload before calling
if (!recipientUrn?.startsWith('urn:li:fs_salesProfile:')) throw new Error('bad recipient');
if (subject.length > 200 || body.length > 1900) throw new Error('payload too long');

Type guard

const isApiErrorBody = (result) => result != null && typeof result === 'object' && !Array.isArray(result) && typeof result.error === 'string' && result.error.length > 0;

Try / catch

try {
  await sendMessage(...);
} catch (err) {
  if (err instanceof CommandExecutionError && / failed$/.test(err.message)) {
    console.error('LinkedIn said:', err.detail ?? err.cause);
  } else throw err;
}

Prevention

When it happens

Trigger: profileResult/creditsResult/sendResult/creditsAfterResult receive a response whose JSON has an error property — e.g. API rejects the createMessage payload, invalid recipient urn, or server-side 4xx surfaced inside the body.

Common situations: Messaging a recipient with InMail restriction; expired credit grant endpoint shape change; LinkedIn API contract drift after a site update; invalid subject/body constraints the API rejects server-side.

Related errors


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