jackwener/OpenCLI · error · EmptyResultError
twitter followers
Error message
twitter followers
What it means
EmptyResultError with command id 'twitter followers' thrown when the target account's follower data appears to indicate a private/restricted timeline. looksLikePrivateTwitterTimeline(lastRawResponse) matched heuristics on the last raw GraphQL payload, so the message specifically warns the account may have restricted its followers list rather than simply having none.
Solutions
- Verify the target account in a browser — if it is protected, its followers list is genuinely unavailable; use a different target.
- Confirm you are logged in with a valid x.com session (ct0 cookie) that can view that account.
- Re-run later in case the account temporarily restricted visibility.
- Do not retry blindly — this is a deterministic access restriction, not a transient failure.
Defensive patterns
Strategy: fallback
Validate before calling
// Pre-check the account's visibility in a browser or via a public profile endpoint before scraping followers
const isProtected = await checkAccountProtected(targetHandle); // your own helper
if (isProtected) throw new Error(`@${targetHandle} protects its followers list; scraping will fail`); Try / catch
import { EmptyResultError } from '@jackwener/opencli/errors';
try {
rows = await opencli.twitter.followers(user, { limit });
} catch (e) {
if (e instanceof EmptyResultError && /private/.test(e.message)) {
return []; // expected: account restricts followers
}
throw e;
} Prevention
- Verify the target account is public before scraping
- Do not blind-retry private-timeline errors — they are deterministic
- Maintain a valid logged-in session for accounts you can legitimately view
- Handle the empty-but-expected case explicitly in pipelines
When it happens
Trigger: Pagination ended with allFollowers.length === 0 and the last raw Followers GraphQL response matched the private-timeline heuristics — the target account protects its followers list, or X returned an access-restricted timeline shell instead of user entries.
Common situations: Scraping a protected/private X account's followers; an account that switched to protected mode since you last scraped it; X serving a restricted response to sessions without sufficient trust; mistakenly targeting a suspended/limited account.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Timeline is private or unavailable to the current X…
- twitter download
- twitter following
- twitter likes
- twitter trending
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9965d17ba6eec722.
Report an issue: GitHub.
Appendix: source
Thrown at clis/twitter/followers.js:198
await page.autoScroll({ times: 1, delayMs: 500 });
try {
await page.waitForCapture(CAPTURE_TIMEOUT_SECONDS);
}
catch {
throw new TimeoutError('twitter followers pagination', CAPTURE_TIMEOUT_SECONDS, `Twitter returned a continuation cursor after ${allFollowers.length} rows, but the next Followers response was not observed.`);
}
await consumeCaptured();
pages++;
if (allFollowers.length === beforeCount && cursor === beforeCursor) {
throw new CommandExecutionError('Twitter followers pagination repeated a cursor without returning new users');
}
}
if (allFollowers.length < limit && cursor && pages >= MAX_PAGINATION_PAGES) {
throw new CommandExecutionError(`Twitter followers pagination exceeded ${MAX_PAGINATION_PAGES} pages before cursor exhaustion`);
}
if (allFollowers.length === 0) {
if (looksLikePrivateTwitterTimeline(lastRawResponse)) {
throw new EmptyResultError('twitter followers', `No follower data returned for @${targetUser} (the target account may have set their followers list to private)`);
}
throw new EmptyResultError('twitter followers', `No followers found for @${targetUser}`);
}
return allFollowers.slice(0, limit);
}
});
export const __test__ = {
extractFollower,
normalizeScreenName,
parseFollowers,
twitterGraphqlError,
};
View on GitHub (pinned to 49907e53dc)