{"record":{"id":"653ca6b0d4d8ed48","repo":"jackwener/OpenCLI","slug":"twitter-followers-api-capture","errorCode":null,"errorMessage":"twitter followers API capture","messagePattern":"twitter followers API capture","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"clis/twitter/followers.js","lineNumber":142,"sourceCode":"                throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');\n            }\n        }\n        if (!targetUser) {\n            throw new ArgumentError('twitter followers user cannot be empty', 'Example: opencli twitter followers @elonmusk --limit 100');\n        }\n\n        await page.installInterceptor('/Followers?');\n        const targetPath = `/${targetUser}/followers`;\n        await page.evaluate(`() => {\n            const targetPath = ${JSON.stringify(targetPath)};\n            window.history.pushState({}, '', targetPath);\n            window.dispatchEvent(new PopStateEvent('popstate', { state: {} }));\n        }`);\n        try {\n            await page.waitForCapture(CAPTURE_TIMEOUT_SECONDS);\n        }\n        catch {\n            throw new TimeoutError('twitter followers API capture', CAPTURE_TIMEOUT_SECONDS, 'No Followers response was observed after opening the followers list.');\n        }\n        const currentPath = unwrapBrowserResult(await page.evaluate('() => window.location.pathname'));\n        if (typeof currentPath !== 'string' || !currentPath.toLowerCase().endsWith('/followers')) {\n            throw new CommandExecutionError('SPA navigation to Twitter followers failed');\n        }\n\n        const allFollowers = [];\n        const seen = new Set();\n        let cursor = null;\n        let lastRawResponse = null;\n        let pages = 0;\n\n        const consumeCaptured = async () => {\n            const requests = await page.getInterceptedRequests();\n            if (!Array.isArray(requests)) {\n                throw new CommandExecutionError('Twitter followers interceptor returned malformed responses');\n            }\n            for (const request of requests) {","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/followers.js#L124-L160","documentation":"The command triggers an SPA navigation to /{user}/followers and waits up to CAPTURE_TIMEOUT_SECONDS (10s) for the intercepted /Followers GraphQL request via page.waitForCapture. If no matching response is observed in time, the wait's rejection is converted into a TimeoutError naming the operation and timeout. This means X never issued (or the interceptor never saw) a Followers API call after the navigation.","triggerScenarios":"The followers page requires login (redirect to /login), the account is suspended/protected so no Followers request fires, the interceptor pattern '/Followers?' no longer matches X's endpoint path, the popstate navigation failed, or the network is slow beyond 10 seconds.","commonSituations":"Target account is private/suspended so X never loads the followers list; X changed the GraphQL operation path; x.com is serving a login wall despite a ct0 cookie; heavy rate-limiting or a network hiccup.","solutions":["Retry — transient slowness often resolves; consider increasing CAPTURE_TIMEOUT_SECONDS","Verify you can open https://x.com/{user}/followers manually in the same browser profile","Try a different target account (the target may be private/suspended)","Update the interceptor pattern in followers.js if X renamed the Followers endpoint","Re-authenticate if the page is redirecting to login"],"exampleFix":"// before\nconst CAPTURE_TIMEOUT_SECONDS = 10;\n// after\nconst CAPTURE_TIMEOUT_SECONDS = 30;","handlingStrategy":"retry","validationCode":"// pre-check that the followers page loads for this account\nconst res = await fetch('https://x.com/' + handle + '/followers', { headers: { cookie } });\nif (res.redirected && res.url.includes('/login')) throw new Error('auth wall for ' + handle);","typeGuard":"null","tryCatchPattern":"try {\n  const rows = await opencli.twitter.followers(user, { limit });\n} catch (err) {\n  if (err instanceof TimeoutError && err.message.includes('API capture')) {\n    await sleep(3000);\n    return opencli.twitter.followers(user, { limit }); // one retry\n  } else throw err;\n}","preventionTips":["Retry transient timeouts with backoff before failing","Skip accounts whose followers page is private/suspended (check manually first)","Raise CAPTURE_TIMEOUT_SECONDS on slow networks","Keep the interceptor pattern updated with X endpoint names"],"tags":["timeout","twitter","network","interception"],"backgroundTag":"api-response-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}