{"record":{"id":"47cad7e0e7afe530","repo":"jackwener/OpenCLI","slug":"twitter-device-follow-fetch-failed-data-detail","errorCode":null,"errorMessage":"Twitter device-follow fetch failed: ${data.detail || 'unknown error'}","messagePattern":"Twitter device-follow fetch failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/device-follow.js","lineNumber":163,"sourceCode":"        });\n        const data = await page.evaluate(`async () => {\n        try {\n          const r = await fetch(\"${apiUrl}\", { method: \"GET\", headers: ${headers}, credentials: 'include' });\n          if (!r.ok) return { error: r.status };\n          try {\n            return await r.json();\n          } catch (e) {\n            return { errorKind: 'non_json', detail: String(e && e.message || e) };\n          }\n        } catch (e) {\n          return { errorKind: 'exception', detail: String(e && e.message || e) };\n        }\n      }`);\n        if (data?.errorKind === 'non_json') {\n            throw new CommandExecutionError(`Twitter device-follow returned non-JSON response: ${data.detail || 'unknown parse error'}`);\n        }\n        if (data?.errorKind === 'exception') {\n            throw new CommandExecutionError(`Twitter device-follow fetch failed: ${data.detail || 'unknown error'}`);\n        }\n        if (data?.error) {\n            if (data.error === 401 || data.error === 403) {\n                throw new AuthRequiredError('x.com', `Twitter device-follow returned HTTP ${data.error}`);\n            }\n            throw new CommandExecutionError(describeTwitterApiError('device_follow', data.error));\n        }\n        const parsed = parseDeviceFollow(data, new Set());\n        if (!parsed) {\n            throw new CommandExecutionError('Twitter device-follow response was missing the expected timeline/globalObjects shape.');\n        }\n        if (parsed.malformedEntries > 0 || parsed.unmatchedTweetEntries > 0) {\n            throw new CommandExecutionError('Twitter device-follow entries could not be joined to tweet/user objects.');\n        }\n        if (parsed.rows.length === 0) {\n            throw new EmptyResultError('twitter device-follow', 'No device-follow notification tweets found.');\n        }\n        const rows = parsed.rows;","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/device-follow.js#L145-L181","documentation":"The device-follow fetch is executed inside the browser page via an injected async function; any exception thrown there (network failure, CORS, aborted request, undefined variable) is caught and returned as errorKind 'exception' with String(e.message || e), then rethrown on the Node side as a CommandExecutionError with the detail appended.","triggerScenarios":"The in-page fetch to the device_follow GraphQL endpoint throws: network interruption, request blocked/aborted, x.com page navigated mid-fetch, or a bug in the injected script (e.g. referencing an undefined helper).","commonSituations":"Flaky network or proxy dropping the request; closing or navigating the browser window while the command runs; x.com frontend changes breaking injected code; extensions (ad blockers) interfering with fetch.","solutions":["Retry the command — many failures are transient network issues","Keep the browser page open and idle until the command completes","Disable ad-blocker/privacy extensions for x.com or use a clean profile","Check data.detail in the message for the underlying exception text","Update the CLI if x.com changed its frontend/API surface"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check: ensure the x.com page is loaded and stable before invoking\nawait page.waitForLoadState('domcontentloaded');","typeGuard":null,"tryCatchPattern":"try {\n  await cli.twitter.deviceFollow();\n} catch (e) {\n  if (e instanceof CommandExecutionError && /fetch failed/.test(e.message)) {\n    // transient in-page exception: back off and retry\n    await sleep(5000);\n    return retry(upTo = 3);\n  }\n  throw e;\n}","preventionTips":["Keep the browser window open and idle while the command runs","Ensure a stable network connection; avoid switching VPN/proxy mid-run","Retry transient fetch failures with backoff","Update the CLI if x.com frontend changes break the injected script"],"tags":["network","browser","fetch","exception"],"backgroundTag":"fetch-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}