jackwener/OpenCLI · error · CommandExecutionError
${describeTwitterApiError('TweetDetail', data.error)}
Error message
${describeTwitterApiError('TweetDetail', data.error)} What it means
thread.js:151 wraps the TweetDetail GraphQL response with throwIfLoginWall and then checks data.error. If the API returned an error payload and no tweets have been collected yet (allTweets.length === 0), it throws CommandExecutionError with a human-readable description from describeTwitterApiError('TweetDetail', data.error). If some tweets were already fetched, it breaks out of pagination instead of throwing.
Source
Thrown at clis/twitter/thread.js:151
'X-Twitter-Auth-Type': 'OAuth2Session',
'X-Twitter-Active-User': 'yes',
});
// Paginate — fetch in browser, parse in TypeScript
const allTweets = [];
const seen = new Set();
let cursor = null;
for (let i = 0; i < 5; i++) {
const apiUrl = buildTweetDetailUrl(tweetId, cursor);
// Browser-side: fetch + JSON parse with HTML-as-JSON sniffer so a
// login wall / WAF page surfaces as a structured LoginWallError
// instead of `SyntaxError: Unexpected token '<'`.
const data = throwIfLoginWall(await page.evaluate(`async () => {
${BROWSER_JSON_SNIFF_FN}
return await fetchJsonOrLoginWall("${apiUrl}", { headers: ${headers}, credentials: 'include' });
}`), { url: apiUrl });
if (data?.error) {
if (allTweets.length === 0)
throw new CommandExecutionError(describeTwitterApiError('TweetDetail', data.error));
break;
}
// TypeScript-side: type-safe parsing + cursor extraction
const { tweets, nextCursor } = parseTweetDetail(data, seen);
allTweets.push(...tweets);
if (!nextCursor || nextCursor === cursor)
break;
cursor = nextCursor;
}
const trimmed = allTweets.slice(0, kwargs.limit);
return applyTopByEngagement(trimmed, kwargs['top-by-engagement']);
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Read the described error in the message (e.g. status 429 vs 403) and address it: wait out rate limits or re-login for auth errors
- Retry later or with fewer requests if it is a rate-limit/429 case; the API is throttling the session
- Update the hard-coded TWEET_DETAIL_QUERY_ID / FEATURES in thread.js to the current values x.com uses (sniff them from the web app's network tab)
- Confirm the tweet still exists and is not protected/deleted — those can produce error payloads on first fetch
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: confirm session still valid with a cheap authenticated endpoint
const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((c) => c.name === 'ct0')?.value;
if (!ct0) throw new Error('Login required before calling thread fetch'); Type guard
function isApiErrorPayload(data) {
return data != null && typeof data === 'object' && 'error' in data;
} Try / catch
import { CommandExecutionError } from '@jackwener/opencli/errors';
try {
const tweets = await fetchThread(url);
} catch (e) {
if (e instanceof CommandExecutionError && /429|rate/i.test(e.message)) {
await sleep(60_000);
return fetchThread(url); // retry with backoff
}
if (e instanceof CommandExecutionError && /40[134]/.test(e.message)) {
console.error('Session/queryId issue: re-login or update TWEET_DETAIL_QUERY_ID.');
} else throw e;
} Prevention
- Throttle requests and add delays between thread fetches to avoid 429s
- Pin and periodically refresh the TweetDetail queryId and FEATURES from live x.com traffic
- Log the raw error status from the API payload to distinguish auth vs rate-limit vs not-found
- Cache fetched threads to reduce repeat calls
When it happens
Trigger: The TweetDetail fetch inside page.evaluate returns { error: <status|message> } — typically an HTTP error status from fetchJsonOrLoginWall (401/403 auth rejection, 429 rate limit, 404 stale queryId), or a GraphQL error body — on the first page of results.
Common situations: Rate limiting after heavy scraping; X changed the GraphQL response shape or retired the hard-coded TWEET_DETAIL_QUERY_ID; session/CSRF token rejected mid-run; deleted or protected tweet where the API returns errors instead of an empty timeline; login wall detected on the first fetch.
Related errors
- twitter_collection_request_error
- ${describeTwitterApiError('SearchTimeline', data.error)}
- ${describeTwitterApiError(endpoint, data.error)}
- ${describeTwitterApiError('UserTweets', data.error)}
- ${probe.detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/ca1bd2c73c4121a3.
Report an issue: GitHub.