{"record":{"id":"934f68fbd93cb16a","repo":"jackwener/OpenCLI","slug":"twitter-device-follow-entries-could-not-be-joined","errorCode":null,"errorMessage":"Twitter device-follow entries could not be joined to tweet/user objects.","messagePattern":"Twitter device-follow entries could not be joined to tweet/user objects\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/device-follow.js","lineNumber":176,"sourceCode":"      }`);\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__ = {\n    buildDeviceFollowUrl,\n    extractEntries,\n    joinEntryToTweet,\n    shapeRow,\n    parseDeviceFollow,\n    parseLimit,\n};","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/device-follow.js#L158-L194","documentation":"parseDeviceFollow returns counts of entries that were malformed or could not be joined back to their tweet/user objects in globalObjects. If any exist, the command refuses to return partial/garbage data and throws CommandExecutionError instead.","triggerScenarios":"The response contains timeline entries whose tweet or user IDs are missing from globalObjects.tweets/globalObjects.users (deleted tweets, pruned users, tombstoned entries), or entries with unexpected internal structure.","commonSituations":"Notifications referencing tweets that were deleted or from suspended accounts; Twitter tombstoning entries in the timeline; partially truncated payloads; new entry types the joiner doesn't recognize.","solutions":["Retry — deleted/tombstoned entries are sometimes transient","Update the CLI so the parser/joiner handles the newest entry shapes","Narrow the query (smaller --limit, different account) to avoid stale notification entries","Inspect the raw payload to confirm which entries fail to join"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// No caller-side pre-check; reduce risk by using smaller limits on accounts with stale notifications\nconst limit = 50;","typeGuard":null,"tryCatchPattern":"try {\n  await cli.twitter.deviceFollow();\n} catch (e) {\n  if (e instanceof CommandExecutionError && /joined to tweet\\/user objects/.test(e.message)) {\n    console.error('Some notifications reference deleted tweets/users — retry or lower --limit.');\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Retry — deleted/tombstoned entries can clear between runs","Prefer smaller --limit windows to skip stale notification entries","Update the CLI for newer entry-shape handling","Check the account's notifications on x.com to confirm expected content"],"tags":["parsing","data-integrity","twitter","schema"],"backgroundTag":"response-parse-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}