{"record":{"id":"9f50b9f127ea0d09","repo":"jackwener/OpenCLI","slug":"malformed-twitter-following-user-missing-screen-n","errorCode":null,"errorMessage":"Malformed Twitter following user: missing screen_name","messagePattern":"Malformed Twitter following user: missing screen_name","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/following.js","lineNumber":74,"sourceCode":"        withBirdwatchNotes: false,\n        withVoice: true,\n        withV2Timeline: true,\n    };\n    if (cursor)\n        vars.cursor = cursor;\n    return `/i/api/graphql/${queryId}/Following`\n        + `?variables=${encodeURIComponent(JSON.stringify(vars))}`\n        + `&features=${encodeURIComponent(JSON.stringify(FEATURES))}`;\n}\n\nfunction extractUser(result) {\n    if (!result || result.__typename !== 'User')\n        return null;\n    const core = result.core || {};\n    const legacy = result.legacy || {};\n    const screenName = core.screen_name || legacy.screen_name || '';\n    if (!screenName) {\n        throw new CommandExecutionError('Malformed Twitter following user: missing screen_name');\n    }\n    return {\n        screen_name: screenName,\n        name: core.name || legacy.name || '',\n        bio: legacy.description || result.profile_bio?.description || '',\n        followers: result.relationship_counts?.followers\n            ?? legacy.followers_count\n            ?? legacy.normal_followers_count\n            ?? 0,\n    };\n}\n\nfunction parseFollowing(data) {\n    const users = [];\n    let nextCursor = null;\n    const instructions = data?.data?.user?.result?.timeline_v2?.timeline?.instructions\n        || data?.data?.user?.result?.timeline?.timeline?.instructions\n        || [];","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/following.js#L56-L92","documentation":"CommandExecutionError 'Malformed Twitter following user: missing screen_name' thrown by extractUser in clis/twitter/following.js. When a user_results.result object passes the __typename === 'User' check, its core.screen_name / legacy.screen_name are both empty, meaning X returned a user entity stripped of its handle. The library treats this as corrupt upstream data and fails loudly rather than emitting a row with an empty screen_name.","triggerScenarios":"Parsing a Following GraphQL timeline where an entryId user- entry's itemContent.user_results.result has __typename 'User' but neither core.screen_name nor legacy.screen_name is populated — called from the user/zero command paths.","commonSituations":"X mid-flight API redesign moving screen_name to a new envelope location; suspended/ghost user entities left in a following list; partially redacted payloads served to lower-trust sessions; A/B payload variants where core is renamed (e.g. new naming while legacy is dropped).","solutions":["Retry the command — ghost/degraded user entities are often transient.","Refresh the x.com session (cookies) so X serves complete user payloads.","If persistent, check whether X changed the User payload shape (core/legacy) and update extractUser to read the new location.","Skip past the offending entry externally by lowering --limit to exclude the malformed tail rows, then report the payload shape change."],"exampleFix":"// before\nconst screenName = core.screen_name || legacy.screen_name || '';\n// after\nconst screenName = core.screen_name || legacy.screen_name || result.core?.user_legacy?.screen_name || '';  // adapt to new X payload envelope","handlingStrategy":"try-catch","validationCode":"// Validate the payload shape you feed to extractUser before it throws\nfunction hasScreenName(result) {\n  return Boolean(result && result.__typename === 'User' &&\n    ((result.core && result.core.screen_name) || (result.legacy && result.legacy.screen_name)));\n}","typeGuard":"function isWellFormedUser(r) {\n  return Boolean(r && r.__typename === 'User' &&\n    typeof (r.core?.screen_name ?? r.legacy?.screen_name) === 'string' &&\n    (r.core?.screen_name ?? r.legacy?.screen_name) !== '');\n}","tryCatchPattern":"import { CommandExecutionError } from '@jackwener/opencli/errors';\ntry {\n  rows = await opencli.twitter.following(user, { limit });\n} catch (e) {\n  if (e instanceof CommandExecutionError && /missing screen_name/.test(e.message)) {\n    return await opencli.twitter.following(user, { limit }); // transient ghost entity: retry once\n  }\n  throw e;\n}","preventionTips":["Filter user_results.result entries through a screen_name presence guard in custom parsing","Retry once on malformed-user errors before failing the pipeline","Track X GraphQL payload-shape changes (core/legacy envelope) proactively","Refresh sessions to avoid degraded/redacted user payloads"],"tags":["schema-validation","api-change","twitter","data-integrity"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}