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
- Re-authenticate the x.com session (log in again) to refresh bearer/csrf tokens.
- Wait and retry if rate limited (429).
- 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.
- 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
- Refresh the x.com session regularly during long fetches
- Respect rate limits; add delays between pages
- Re-resolve GraphQL query IDs when Twitter rotates them
- Surface the described HTTP status (401/403/429) to decide the fix
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
- API_ERROR
- API_ERROR
- HTTP ${result.httpStatus} from /api/user
- ${batch.error}
- Suno generate failed (HTTP ${status}): ${detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ccb975dd2f7b1b1e.
Report an issue: GitHub.