jackwener/OpenCLI · error · CommandExecutionError
describeTwitterApiError('ListLatestTweetsTimeline', data.err
Error message
describeTwitterApiError('ListLatestTweetsTimeline', data.error, 'list may be private') What it means
While paginating the list timeline, the ListLatestTweetsTimeline GraphQL endpoint returned an error payload. If this happened on the first page (allTweets empty), the library surfaces it via describeTwitterApiError with the hint 'list may be private'. Subsequent-page errors just break pagination and return the tweets collected so far.
Source
Thrown at clis/twitter/list-tweets.js:187
'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
'X-Twitter-Active-User': 'yes',
});
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(100, limit - allTweets.length + 10);
const apiUrl = buildUrl(queryId, listId, fetchCount, cursor);
const data = throwIfLoginWall(await page.evaluate(`async () => {
${BROWSER_JSON_SNIFF_FN}
return await fetchJsonOrLoginWall(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });
}`), { url: apiUrl });
if (data?.error) {
if (allTweets.length === 0)
throw new CommandExecutionError(describeTwitterApiError('ListLatestTweetsTimeline', data.error, 'list may be private'));
break;
}
const { tweets, nextCursor } = parseListTimeline(data, seen);
allTweets.push(...tweets);
if (!nextCursor || nextCursor === cursor || tweets.length === 0)
break;
cursor = nextCursor;
}
const trimmed = allTweets.slice(0, limit);
return applyTopByEngagement(trimmed, kwargs['top-by-engagement']);
},
});
View on GitHub (pinned to 49907e53dc)
Solutions
- Read the concrete status/detail embedded by describeTwitterApiError in the message (e.g. 404 → wrong id; 401/403 → private or logged out)
- Re-login to x.com in the controlled browser if the error is auth-flavored
- Verify the list id via `opencli twitter lists` and that your account can see it on x.com
- Slow down / retry later on 429; update the library on 400 (queryId expired)
Example fix
// before
const tweets = await run('twitter', 'list-tweets', { listId }); // throws on first-page API error
// after
try {
const tweets = await run('twitter', 'list-tweets', { listId });
} catch (e) {
if (e.message.includes('list may be private')) {
console.error(`No access to list ${listId}: check it exists and your account can view it.`);
} else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
const lists = await run('twitter', 'lists', { limit: 200 });
if (!lists.some(l => String(l.id) === String(listId))) {
throw new Error(`List ${listId} not visible to your account (deleted, private, or wrong id).`);
} Type guard
const apiErrored = (d) => d && typeof d === 'object' && 'error' in d;
Try / catch
try {
const tweets = await run('twitter', 'list-tweets', { listId });
} catch (e) {
if (/list may be private/.test(e.message)) {
console.error(`Cannot read list ${listId}: verify it exists and your account has access.`);
} else if (/429/.test(e.message)) {
await sleep(60000); return run('twitter', 'list-tweets', { listId });
} else throw e;
} Prevention
- Verify list access on x.com in the same account before scripting
- Update the library when you see 400s (GraphQL queryId rotation)
- Throttle request rates to avoid 429s
- Only first-page errors throw; partial results on later-page errors are by design
When it happens
Trigger: First fetch of the ListLatestTweetsTimeline endpoint returned data.error — e.g. HTTP 401/403 (no access to a private list), 404 (list deleted or wrong id), 429 (rate limit), or 400 (stale queryId) — propagated by fetchJsonOrLoginWall through throwIfLoginWall.
Common situations: Reading a list owned by someone else that is private; list id typo → 404; heavy scraping tripping X rate limits; twitter-openapi queryId outdated after X rotates GraphQL ids; session cookies half-expired so the API treats the call as logged out.
Related errors
- describeTwitterApiError('ListsManagementPageTimeline', data.
- File could not be read: ${path}
- PAID_CONTENT
- ${wantModel} model requires a paid Claude plan.
- Douyin API auth/permission error ${code} at ${method} ${url}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2dbab80cf8b0df13.
Report an issue: GitHub.