{"record":{"id":"9965d17ba6eec722","repo":"jackwener/OpenCLI","slug":"twitter-followers","errorCode":null,"errorMessage":"twitter followers","messagePattern":"twitter followers","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/twitter/followers.js","lineNumber":198,"sourceCode":"            await page.autoScroll({ times: 1, delayMs: 500 });\n            try {\n                await page.waitForCapture(CAPTURE_TIMEOUT_SECONDS);\n            }\n            catch {\n                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.`);\n            }\n            await consumeCaptured();\n            pages++;\n            if (allFollowers.length === beforeCount && cursor === beforeCursor) {\n                throw new CommandExecutionError('Twitter followers pagination repeated a cursor without returning new users');\n            }\n        }\n        if (allFollowers.length < limit && cursor && pages >= MAX_PAGINATION_PAGES) {\n            throw new CommandExecutionError(`Twitter followers pagination exceeded ${MAX_PAGINATION_PAGES} pages before cursor exhaustion`);\n        }\n        if (allFollowers.length === 0) {\n            if (looksLikePrivateTwitterTimeline(lastRawResponse)) {\n                throw new EmptyResultError('twitter followers', `No follower data returned for @${targetUser} (the target account may have set their followers list to private)`);\n            }\n            throw new EmptyResultError('twitter followers', `No followers found for @${targetUser}`);\n        }\n        return allFollowers.slice(0, limit);\n    }\n});\n\nexport const __test__ = {\n    extractFollower,\n    normalizeScreenName,\n    parseFollowers,\n    twitterGraphqlError,\n};\n","sourceCodeStart":180,"sourceCodeEnd":212,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/followers.js#L180-L212","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Pre-check the account's visibility in a browser or via a public profile endpoint before scraping followers\nconst isProtected = await checkAccountProtected(targetHandle); // your own helper\nif (isProtected) throw new Error(`@${targetHandle} protects its followers list; scraping will fail`);","typeGuard":null,"tryCatchPattern":"import { EmptyResultError } from '@jackwener/opencli/errors';\ntry {\n  rows = await opencli.twitter.followers(user, { limit });\n} catch (e) {\n  if (e instanceof EmptyResultError && /private/.test(e.message)) {\n    return []; // expected: account restricts followers\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["empty-result","privacy","twitter","scraping"],"backgroundTag":"empty-result-set","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}