jackwener/OpenCLI · error · CommandExecutionError

Likes

Error message

Likes

What it means

CommandExecutionError produced by describeTwitterApiError('Likes', data.error) when the first Likes GraphQL request fails outright (HTTP error status returned inside the page fetch) and nothing has been collected yet. The library surfaces the API's error status/code through the shared describer so the user sees the underlying Twitter API failure.

Source

Thrown at clis/twitter/likes.js:308

        let outputCount = useOutputFile ? jsonlState.count : 0;
        let cursor = resumed?.cursor || null;
        let lastRawResponse = null;
        let pages = 0;
        let exhausted = false;
        // Runaway guard only; --limit/--all and cursor exhaustion control normal pagination.
        while (pages < maxPages && (fetchAll || allTweets.length < limit)) {
            pages += 1;
            const currentCount = useOutputFile ? outputCount : allTweets.length;
            const remaining = fetchAll ? 100 : (limit - currentCount + 10);
            const fetchCount = Math.min(100, remaining);
            const apiUrl = buildLikesUrl(likesQueryId, userId, fetchCount, cursor);
            const data = unwrapBrowserResult(await page.evaluate(`async () => {
        const r = await fetch(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });
        return r.ok ? await r.json() : { error: r.status };
      }`));
            if (data?.error) {
                if ((useOutputFile ? outputCount : allTweets.length) === 0)
                    throw new CommandExecutionError(describeTwitterApiError('Likes', data.error));
                break;
            }
            lastRawResponse = data;
            const hasInstructions = Array.isArray(data?.data?.user?.result?.timeline_v2?.timeline?.instructions)
                || Array.isArray(data?.data?.user?.result?.timeline?.timeline?.instructions);
            if (!hasInstructions) {
                if (looksLikePrivateTwitterTimeline(data) && (useOutputFile ? outputCount : allTweets.length) === 0) {
                    throw new EmptyResultError('twitter likes', `No likes returned for @${username} (Likes are private by default on X; only the account owner can view their own likes)`);
                }
                throw new CommandExecutionError('twitter_likes_protocol_error: missing Likes timeline instructions');
            }
            const { tweets, nextCursor } = parseLikes(data, seen);
            if (useOutputFile) {
                appendJsonlRows(outputFile, tweets);
                outputCount += tweets.length;
            }
            else {
                allTweets.push(...tweets);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-authenticate the x.com session (log in again) to refresh bearer/csrf tokens.
  2. Wait and retry if rate limited (429).
  3. Update LIKES_QUERY_ID (and FEATURES) if Twitter rotated the GraphQL query ID, by extracting the current ID from x.com's main bundle via resolveTwitterQueryId.
  4. Check describeTwitterApiError output for the exact HTTP status and address it (401->login, 403->permissions/protected account).

Example fix

// before
const LIKES_QUERY_ID = 'CDWHmpZeSdIJ3HGeRbNm0w'; // stale, 400 from API
// after
const LIKES_QUERY_ID = await resolveTwitterQueryId(page, 'Likes', 'CDWHmpZeSdIJ3HGeRbNm0w'); // resolve fresh from x.com bundle
Defensive patterns

Strategy: retry

Validate before calling

// preflight: verify the session can reach the GraphQL endpoint
const probe = await page.evaluate(() => fetch('/i/api/graphql/.../Likes?...', { credentials: 'include' }).then(r => r.status));
if (probe === 401 || probe === 403) await reauthenticate();

Type guard

null

Try / catch

try {
  await cli.twitter.likes({ username });
} catch (e) {
  if (/Likes.*error|describeTwitterApiError/.test(e.constructor.name + e.message) && e.status === 429) {
    await sleep(backoff);
    return retry();
  }
  if (e.status === 401) await reauthenticate();
  throw e;
}

Prevention

When it happens

Trigger: The in-page fetch of the Likes GraphQL endpoint returns r.ok === false (data = { error: r.status }), and the run has zero collected likes so far (useOutputFile ? outputCount : allTweets.length === 0) at clis/twitter/likes.js:308.

Common situations: Expired auth/ct0 token giving 401/403; Twitter rate limiting (429); stale hardcoded LIKES_QUERY_ID after Twitter rotated GraphQL query IDs (400); Cloudflare/anomaly challenge pages.

Related errors


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