jackwener/OpenCLI · error · CommandExecutionError

Following

Error message

Following

What it means

CommandExecutionError with the message built by describeTwitterApiError('Following', status). Thrown when the Following GraphQL request fails with a status other than 401/403; the helper turns the raw numeric status into a human-readable explanation for the Following endpoint.

Source

Thrown at clis/twitter/following.js:223

            throw new CommandExecutionError(`Could not find user @${targetUser}`);

        const allUsers = [];
        const seen = new Set();
        let cursor = null;
        let lastRawResponse = null;

        // Runaway guard only; --limit and cursor exhaustion control normal pagination.
        for (let i = 0; i < MAX_PAGINATION_PAGES && allUsers.length < limit; i++) {
            const fetchCount = Math.min(50, limit - allUsers.length + 10);
            const apiUrl = buildFollowingUrl(followingQueryId, userId, fetchCount, cursor);
            const data = unwrapBrowserResult(await page.evaluate(async (url, headers) => {
                const r = await fetch(url, { headers, credentials: 'include' });
                return r.ok ? await r.json() : { error: r.status };
            }, apiUrl, headers));
            if (data?.error) {
                if (data.error === 401 || data.error === 403)
                    throw new AuthRequiredError('x.com', `Twitter following request failed (HTTP ${data.error})`);
                throw new CommandExecutionError(describeTwitterApiError('Following', data.error));
            }
            lastRawResponse = data;
            const { users, nextCursor } = parseFollowing(data);
            for (const u of users) {
                if (!seen.has(u.screen_name)) {
                    seen.add(u.screen_name);
                    allUsers.push(u);
                }
            }
            if (!nextCursor || nextCursor === cursor)
                break;
            cursor = nextCursor;
        }

        if (allUsers.length === 0) {
            if (looksLikePrivateTwitterTimeline(lastRawResponse)) {
                throw new EmptyResultError('twitter following', `No following data returned for @${targetUser} (the target account may have set their following list to private)`);
            }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the described status: 429 means slow down — wait before retrying
  2. Update opencli so resolveTwitterQueryId fetches the current Following query ID
  3. Retry later for 5xx (Twitter-side incident)
  4. Lower --limit and add delays between runs to stay under rate limits

Example fix

// before: hammering the API -> 429
Error: Twitter Following request failed: HTTP 429
// after: wait, then limit results
$ sleep 300 && opencli twitter following @elonmusk --limit 50
Defensive patterns

Strategy: retry

Validate before calling

// Throttle calls up front to avoid 429s
const sleep = ms => new Promise(r => setTimeout(r, ms));
for (const u of targets) {
  await cli.following(u, { limit: 100 });
  await sleep(15000); // stay under Twitter rate limits
}

Type guard

function isRetryableApiError(err) {
  const code = (/(\d{3})/.exec(String(err && err.message)) || [])[1];
  return code === '429' || (code && code.startsWith('5'));
}

Try / catch

try {
  await cli.following(target);
} catch (err) {
  if (isRetryableApiError(err)) {
    const code = (/(\d{3})/.exec(err.message))[1];
    await sleep(code === '429' ? 300000 : 5000);
    return cli.following(target);
  }
  throw err;
}

Prevention

When it happens

Trigger: The in-page fetch of the Following API URL returns e.g. 400, 404, 429, or 5xx; data.error is truthy and not 401/403, so describeTwitterApiError('Following', data.error) is thrown, typically during pagination.

Common situations: Rate limiting (429) after aggressive pagination; stale FOLLOWING_QUERY_ID producing 400 Bad Request; Twitter 5xx outage; account restrictions returning 404 for the following tab.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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