jackwener/OpenCLI · error · CommandExecutionError
${describeTwitterApiError(endpoint, data.error)}
Error message
${describeTwitterApiError(endpoint, data.error)} What it means
timeline.js:194 fetches a timeline page inside page.evaluate and converts non-OK responses to { error: r.status }. If data.error is present and no tweets were collected yet (allTweets.length === 0), it throws CommandExecutionError described by describeTwitterApiError(endpoint, data.error); otherwise pagination just stops with what was gathered.
Source
Thrown at clis/twitter/timeline.js:194
'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;
// Runaway guard only; --limit and cursor exhaustion control normal pagination.
for (let i = 0; i < MAX_PAGINATION_PAGES && allTweets.length < limit; i++) {
const fetchCount = Math.min(40, limit - allTweets.length + 5); // over-fetch slightly for promoted filtering
const variables = buildTimelineVariables(timelineType, fetchCount, cursor);
const apiUrl = buildHomeTimelineUrl(queryId, endpoint, variables);
const data = await page.evaluate(`async () => {
const r = await fetch("${apiUrl}", { method: "${method}", headers: ${headers}, credentials: 'include' });
return r.ok ? await r.json() : { error: r.status };
}`);
if (data?.error) {
if (allTweets.length === 0)
throw new CommandExecutionError(describeTwitterApiError(endpoint, data.error));
break;
}
const { tweets, nextCursor } = parseHomeTimeline(data, seen);
allTweets.push(...tweets);
if (!nextCursor || nextCursor === cursor)
break;
cursor = nextCursor;
}
const trimmed = allTweets.slice(0, limit);
return applyTopByEngagement(trimmed, kwargs['top-by-engagement']);
},
});
export const __test__ = {
buildTimelineVariables,
buildHomeTimelineUrl,
parseHomeTimeline,
};
View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the status code in the message: 429 → back off and slow down requests; 401/403 → re-login to refresh ct0/session
- Reduce request frequency and add page delays between timeline pages
- Update the endpoint's queryId (fallbackQueryId in TIMELINE_ENDPOINTS) to the current value from x.com's web app network traffic
- If errors appear only after some pages, rely on the partial results — the command breaks instead of throwing once tweets exist
Defensive patterns
Strategy: retry
Validate before calling
// Ensure session before paginating
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some((c) => c.name === 'ct0' && c.value)) throw new Error('Login required');
// Validate the flag combo up front
if (!Number.isInteger(limit) || limit < 1) throw new Error('limit must be a positive integer'); Type guard
function hasTimelineData(data) {
return data != null && typeof data === 'object' && !('error' in data)
&& Array.isArray(data?.data?.home?.home_timeline_urt?.instructions ?? []);
} Try / catch
import { CommandExecutionError } from '@jackwener/opencli/errors';
try {
const tweets = await fetchTimeline('home', { limit: 100 });
} catch (e) {
if (e instanceof CommandExecutionError && /429/.test(e.message)) {
await sleep(5 * 60_000); // back off on rate limit, then retry
} else if (e instanceof CommandExecutionError) {
console.error(`Timeline API failed (${e.message}); check session or queryId.`);
} else throw e;
} Prevention
- Increase --page-delay and reduce --limit to stay under rate limits
- Refresh endpoint queryIds when X rotates them
- Persist partial results so a mid-run failure doesn't lose data
- Monitor for 401/403 and re-login proactively before long jobs
When it happens
Trigger: The timeline fetch returns a non-OK HTTP status (401/403 session rejected, 429 rate limited, 404 queryId retired) or a GraphQL error body on the first page of the selected TIMELINE_ENDPOINTS entry.
Common situations: Aggressive polling tripping x.com rate limits; stale/rotated queryId for the endpoint; expired auth mid-run; X changing response schema so parsing fails upstream and error shapes surface; protected/private accounts in UserTweets-like endpoints.
Related errors
- twitter_collection_request_error
- ${describeTwitterApiError('SearchTimeline', data.error)}
- ${describeTwitterApiError('TweetDetail', data.error)}
- ${describeTwitterApiError('UserTweets', data.error)}
- ${probe.detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/458d949761bb0f55.
Report an issue: GitHub.