{"record":{"id":"1d1c49d62ba4c5ec","repo":"jackwener/OpenCLI","slug":"device-follow","errorCode":null,"errorMessage":"device_follow","messagePattern":"device_follow","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/device-follow.js","lineNumber":169,"sourceCode":"            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;\n        const trimmed = rows.slice(0, limit);\n        return applyTopByEngagement(trimmed, kwargs['top-by-engagement']);\n    },\n});\n\nexport const __test__ = {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/device-follow.js#L151-L187","documentation":"For any other GraphQL error status (not 401/403, not a parse/exception failure), the command builds a human-readable message via describeTwitterApiError('device_follow', data.error) and throws CommandExecutionError. The raw message 'device_follow' is the API name passed into that describer, so the thrown text summarizes an API-level error for the device_follow endpoint.","triggerScenarios":"The device_follow GraphQL endpoint returns a JSON body with an error field whose code/status is anything other than 401 or 403 (e.g. 404, 429 rate limit, 5xx, or a GraphQL errors array code).","commonSituations":"Hitting rate limits (429) during heavy scraping; Twitter deprecating or relocating the device_follow endpoint (404); transient 5xx server errors; account restrictions returning unusual codes.","solutions":["Read the full describeTwitterApiError message for the specific code","Back off and retry with lower frequency if the code indicates rate limiting (429)","Check whether the endpoint was changed/deprecated and update the CLI","Retry later for 5xx codes — often a Twitter-side incident"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-check not possible for API-side codes; rate-limit friendly pacing instead\nconst sleepMs = Number(process.env.TWITTER_MIN_INTERVAL_MS || 2000);","typeGuard":null,"tryCatchPattern":"try {\n  await cli.twitter.deviceFollow();\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('429')) {\n    await sleep(15 * 60_000); // rate limited: long backoff\n    return retryOnce();\n  }\n  if (e instanceof CommandExecutionError) throw e; // other API codes: inspect message\n  throw e;\n}","preventionTips":["Space out x.com API calls to avoid 429 rate limits","Log the full describeTwitterApiError message to identify the code","Watch for x.com endpoint deprecations and keep the CLI updated","Treat 5xx codes as transient and retry later"],"tags":["api","rate-limit","graphql","twitter"],"backgroundTag":"twitter-api-error","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}